## Load libraries
library(ArchR)
library(knitr)
library(rhdf5)
library(uwot)
library(tidyverse)
## Warning in system("timedatectl", intern = TRUE): running command 'timedatectl'
## had status 1
library(scater)
library(zellkonverter)
#library(caret)
h5disableFileLocking()
The scATAC-seq dataset contains two replicates each from 4 stages of mouse gastrulation, E7.5, E8.0, E8.5 and E8.75. We will use the mm10 Genome, because that is the genome.
# names of gastrulation stages
stages <- c("E7.5_rep1", "E7.5_rep2", "E8.0_rep1", "E8.0_rep2",
"E8.5_rep1", "E8.5_rep2", "E8.75_rep1", "E8.75_rep2")
# combine all raw fragment files
inputFiles <- setNames(c("raw_files/E7.5_rep1_atac_fragments.tsv.gz",
"raw_files/E7.5_rep2_atac_fragments.tsv.gz",
"raw_files/E8.0_rep1_atac_fragments.tsv.gz",
"raw_files/E8.0_rep2_atac_fragments.tsv.gz",
"raw_files/E8.5_rep1_atac_fragments.tsv.gz",
"raw_files/E8.5_rep2_atac_fragments.tsv.gz",
"raw_files/E8.75_rep1_atac_fragments.tsv.gz",
"raw_files/E8.75_rep2_atac_fragments.tsv.gz"),
c("E7.5_rep1", "E7.5_rep2", "E8.0_rep1", "E8.0_rep2",
"E8.5_rep1", "E8.5_rep2", "E8.75_rep1", "E8.75_rep2"))
addArchRGenome("mm10")
Create Arrow Files
We will read in the accessible fragments from the fragments file. QC information is automatically computed for each cell. Based on visual inspection of TSS Enrichment score vs log10(nFrags) I used a threshold of at least 1000 unique fragments and a TSS Enrichment score of at least 2.75. At the beginning it is better to be less stringent, because we can still filter the data at a later stage. The cells are filtered based on these threshold and data are stored in arrow files. Genome-wide tile matrices are created using 500-bp bins. The tile matrix T is binary matrix and contains information whether in a particular 500-bp bin/tile i there were any insertions in a particular cell j.
\(T \in \mathbb{R}^{NXD}\) with \(T_{i,j} = 1\) if an insertion is present in tile \(i\) and cell \(j\). \(T_{i,j} = 0\) if no insertion is present in that cell, meaning \(T\) is a binary matrix.
In addition a GeneScoreMatrix is created.
I set the size of the maximum number of fragments included to 1e+07, instead of the default 1e+05, because we cut off some cells otherwise.
ArrowFiles <- createArrowFiles(
inputFiles = inputFiles,
sampleNames = names(inputFiles),
minTSS =2.75, #Dont set this too high because you can always increase later
minFrags = 1000, # minimum number of mapped fragments required
# count matrix, instead of using peak it uses fixed-width sliding window of bins across the whole genome
addTileMat = TRUE,
addGeneScoreMat = TRUE, # uses signal proximal to the TSS to estimate gene activity
subThreading = FALSE,
maxFrags = 1e+07,
minFragSize = 10,
maxFragSize = 2000,
QCDir = "QualityControl",
force = FALSE, # change to TRUE if you want to ovewrite the files
# the length in bp that wraps around nucleosomes ->
#identify fragments as sub-nucleosome spanning, mono-nucleosome spanning or multi-nucleosome spanning
nucLength = 147,
# integer vector -> define region up/downstream of TSS to include as promoter region
# can be used to calculate e.g fraction of reads in promoter (FIP)
promoterRegion = c(2000, 100),
# parameters for computing TSS enrichment scores, window (bp) centered at TSS = 101
# flanking window = 2000 bp
# norm = size of flank window used for normalization = 100 bp
# accessibility within 101 bp surrounding the TSS will be normalized to accessibility
# in 100 bp bins from -2000:-1901bp and 1901: 2000
TSSParams = list(101, 2000, 100),
# which chromosomes to exclude form downstream analysis
# in human and mouse: mitochondrial DNA (chrM) and male sex chromosome (chrY)
# the fragments are still stored in the arrow files
excludeChr = c("chrM", "chrY"),
# number of chunks to divide chromosomes in -> low-memory parallelized reading of input files
nChunk = 5,
# name of field in input bam file containing the barcode tag information
bcTag = "qname",
offsetPlus = 4, # offset applied to + stranded Tn5 insertion -> account for precise Tn5 binding site
offsetMinus = -5,
logFile = createLogFile("createArrows")
)
Inferring Doublets
Should be removed, because they can interfere with downstream analysis.
Doublet detection-and-removal algorithm: Heterotypic doublets are identified by generating a collection of synthetic doublets. These synthetic doublets are projected into low-dimensional embeddings. Searching for nearest neighbours to the synthetic doublets we can identify doublets in the dataset. This outperforms the prediction of doublets using fragment number (ROC-AUC). (Compared to demuxlet as ground truth)
We can also identify doublets in the scRNA-seq space if we have paired data and remove the cells in this way.
# for each sample provided doublet information will be assigned to each cell
# this way we can remove doublet-based clusters downstream
doubScores <- addDoubletScores(
useMatrix = "TileMatrix",
input = ArrowFiles,
k = 10, #Refers to how many cells near a "pseudo-doublet" to count.
nTrial = 5, # number of time to simulate nCell doublets
knnMethod = "UMAP", #Refers to the dimensionality reduciton method to use for nearest neighbor search.
LSIMethod = 1 # oder of normalization: tf-log(idf)
)
Create ArchRProject
An ArchR Project is initialized with some important attributes:
- ouput directory
- sample names
sampleColData -> matrix containing data for each sample
cellColData -> contains data associated with each cell
- after using
addDoubletScore() there will be a column for “Doublet Enrichment” and “Doublet Score”
- total number of cells (excluding doublets)
- median TSS score & median number of fragments across all cells and samples
proj <- ArchRProject(
ArrowFiles = ArrowFiles,
outputDirectory = "gastrArchROutput",
copyArrows = TRUE, #This is recommened so that you maintain an unaltered copy for later usage.
geneAnnotation = getGeneAnnotation(),
#genomeAnnotation = getGeneAnnotation(),
showLogo = FALSE
)
proj
# read in existing project, so we don't have to rerun the creation
#saveArchRProject(proj, outputDirectory = "01_FirstQC_ArchRobject/", load = FALSE)
proj <- loadArchRProject(path = "01_FirstQC_ArchRobject/")
Quality Control
Have a look at this for additional QC! https://bioconductor.org/packages/devel/bioc/vignettes/ATACseqQC/inst/doc/ATACseqQC.html
- the number of unique nuclear fragments (as opposed to mitochondrial fragments) A cell with very few usable fragments will not provide enough data to make useful conclusions.
- signal-to-background ratio: if this is low this probably corresponds to dying cells where the entire genome allows random transposition
- fragment size distribution: since 147 bp are wrapped around a nucleosome it is expected that there are depletions of fragments of this length at regular intervals. We expect to see a periodic distribution of fragmetn size corresponding to nucleosomes (mono, di, tri, …), because Tn5 cannot cut DNA that is tightly wrapped around a nucleosome.
Data before QC and corresponding plots are saved in the Quality Control output folder.
log10(unique fragments) vs TSS enrichment
before QC
We used a TSS enrichment score of > 2.75 and a number of unique fragments > 1000 (log10(1000) = 3). It might make sense to use different thresholds for different samples.
dir <- "QualityControl/"
#all_files <- list.files(path)
beforeQC <- map(stages, function(n) {
path <- paste0(dir, n, "/")
all_files <- list.files(path)
file <- all_files[grepl(paste0(n, "-Pre-Filter-Metadata.rds"), all_files)]
# read in file for sample n containing original data before QC
df <- as.data.frame(readRDS(paste0(dir, n, "/", file)))
df %>% mutate(Sample = n) %>%
ggplot(aes(x = log10(nFrags), y = TSSEnrichment)) +
geom_density2d_filled() +
geom_hline(yintercept = 2.75, color="green", linetype="dashed") +
geom_vline(xintercept = log10(1000), color="green", linetype="dashed") +
#geom_xsidedensity(aes(x=log10(pre_filter_meta$nFrags))) +
#geom_ysidedensity(aes(y = pre_filter_meta$TSSEnrichment)) +
ylim(c(2.5, 4.5)) +
facet_wrap(~Sample) +
theme(legend.position = "none") +
labs(x = "Log10 Unique Fragments", y = "TSS Enrichment Score")
})
gridExtra::grid.arrange(grobs = beforeQC, ncol = 2)
## Warning: Removed 361 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 382 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 124 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 103 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 75 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 62 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 269 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 277 rows containing non-finite values (stat_density2d_filled).

after QC
metadata <- as.data.frame(getCellColData(proj))
metadata %>% head
## Sample TSSEnrichment ReadsInTSS ReadsInPromoter
## E7.5_rep2#TGCTGGATCAAGCTAC-1 E7.5_rep2 3.428 32719 132988
## E7.5_rep2#GCCACTAAGTGAGCAA-1 E7.5_rep2 2.759 12761 61758
## E7.5_rep2#ATTACGTCATGGCCCA-1 E7.5_rep2 2.945 13192 60823
## E7.5_rep2#GATTCAGGTAACTACG-1 E7.5_rep2 3.365 15631 64299
## E7.5_rep2#GTGCTGATCCCTCGCA-1 E7.5_rep2 2.810 6218 30111
## E7.5_rep2#CTGCAATAGTAACGAG-1 E7.5_rep2 2.996 7454 33941
## ReadsInBlacklist PromoterRatio PassQC
## E7.5_rep2#TGCTGGATCAAGCTAC-1 17749 0.15421294 1
## E7.5_rep2#GCCACTAAGTGAGCAA-1 14089 0.07720696 1
## E7.5_rep2#ATTACGTCATGGCCCA-1 14215 0.09650094 1
## E7.5_rep2#GATTCAGGTAACTACG-1 7636 0.15642090 1
## E7.5_rep2#GTGCTGATCCCTCGCA-1 8230 0.07454879 1
## E7.5_rep2#CTGCAATAGTAACGAG-1 7397 0.09159430 1
## NucleosomeRatio nMultiFrags nMonoFrags nFrags
## E7.5_rep2#TGCTGGATCAAGCTAC-1 1.547911 89468 169230 431183
## E7.5_rep2#GCCACTAAGTGAGCAA-1 2.864000 89587 103507 399951
## E7.5_rep2#ATTACGTCATGGCCCA-1 1.202697 59343 143071 315142
## E7.5_rep2#GATTCAGGTAACTACG-1 1.288291 40144 89819 205532
## E7.5_rep2#GTGCTGATCCCTCGCA-1 1.069593 35346 97582 201955
## E7.5_rep2#CTGCAATAGTAACGAG-1 1.177268 32949 85097 185279
## nDiFrags DoubletScore DoubletEnrichment
## E7.5_rep2#TGCTGGATCAAGCTAC-1 172485 0.00000 0.1000000
## E7.5_rep2#GCCACTAAGTGAGCAA-1 206857 19.43591 2.8166667
## E7.5_rep2#ATTACGTCATGGCCCA-1 112728 0.00000 0.7333333
## E7.5_rep2#GATTCAGGTAACTACG-1 75569 0.00000 0.2333333
## E7.5_rep2#GTGCTGATCCCTCGCA-1 69027 132.05092 6.5000000
## E7.5_rep2#CTGCAATAGTAACGAG-1 67233 69.25591 4.7000000
## BlacklistRatio
## E7.5_rep2#TGCTGGATCAAGCTAC-1 0.02058175
## E7.5_rep2#GCCACTAAGTGAGCAA-1 0.01761341
## E7.5_rep2#ATTACGTCATGGCCCA-1 0.02255333
## E7.5_rep2#GATTCAGGTAACTACG-1 0.01857618
## E7.5_rep2#GTGCTGATCCCTCGCA-1 0.02037583
## E7.5_rep2#CTGCAATAGTAACGAG-1 0.01996179
metadata %>% group_by(Sample) %>% summarise(n = n())
## # A tibble: 8 × 2
## Sample n
## <chr> <int>
## 1 E7.5_rep1 8708
## 2 E7.5_rep2 12919
## 3 E8.0_rep1 7024
## 4 E8.0_rep2 5943
## 5 E8.5_rep1 10955
## 6 E8.5_rep2 11091
## 7 E8.75_rep1 4002
## 8 E8.75_rep2 5553
metadata %>% ggplot() +
geom_density2d_filled(aes(x = log10(nFrags), y = TSSEnrichment)) +
geom_hline(yintercept = 2.75, color="green", linetype="dashed") +
geom_vline(xintercept = log10(1000), color="green", linetype="dashed") +
#geom_xsidedensity(aes(x=log10(pre_filter_meta$nFrags))) +
#geom_ysidedensity(aes(y = pre_filter_meta$TSSEnrichment)) +
ylim(c(2.5, 4.5)) +
facet_wrap(~Sample) +
theme(legend.position = "none") +
labs(x = "Log10 Unique Fragments", y = "TSS Enrichment Score")
## Warning: Removed 246 rows containing non-finite values (stat_density2d_filled).

ArchR plots
```#{r, fig.width=8, fig.height=15} #create 3 separate dataframes for all samples samples <- map(unique(proj\(Sample), function(name){ index <- BiocGenerics::which(proj\)Sample %in% name) cells <- proj$cellNames[index] sample_subset <- proj[cells] df <- getCellColData(sample_subset, select = c(“log10(nFrags)”, “TSSEnrichment”)) p <- ggPoint( x = df[, 1], y = df[, 2], colorDensity = TRUE, # should the density of points on the plot be indicated by color? continuousSet = “sambaNight”, xlabel = “Log10 unique fragments”, ylabel = “TSS enrichment”, title = paste0(“Sample:”, name), #xlim = c(log10(500), quantile(df[,1], probs = 0.99)), #ylim = c(0, quantile(df[,2], probs = 0.99)) ) + geom_hline(yintercept = 2.75, lty = “dashed”) + geom_vline(xintercept = 3, lty = “dashed”) })
#do.call(“grid.arrange”, c(samples, ncol=4))
gridExtra::grid.arrange(grobs = samples, ncol = 2)
### Plotting sample statistics
* when we have distinct samples, it can be important to compare various
metric between samples
* ridge plots & violin plots are used for grouped data
```r
p1 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
geom_density(aes(x = TSSEnrichment, fill = Sample), alpha = 0.8)
## Warning: `as_data_frame()` was deprecated in tibble 2.0.0.
## Please use `as_tibble()` instead.
## The signature and semantics have changed, see `?as_tibble`.
p2 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
ggridges::geom_density_ridges(aes(x = TSSEnrichment, y = Sample,
fill = Sample), alpha = 0.8) +
theme(legend.position = "none")
p3 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
geom_violin(aes(x = Sample, y = TSSEnrichment, fill = Sample), alpha = 0.8) +
geom_boxplot(aes(x = Sample, y = TSSEnrichment,fill = Sample), alpha = 0.4) +
theme(legend.position = "none") +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none") +
labs(title = "TSS Enrichment")
cowplot::plot_grid(p3, p2, p1, ncol = 3)

p1 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
geom_density(aes(x = log10_nFrags, fill = Sample), alpha = 0.8)
p2 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
ggridges::geom_density_ridges(aes(x = log10_nFrags, y = Sample,
fill = Sample), alpha = 0.8) +
theme(legend.position = "none")
p3 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
geom_violin(aes(x = Sample, y = log10_nFrags, fill = Sample), alpha = 0.8) +
geom_boxplot(aes(x = Sample, y = log10_nFrags,fill = Sample), alpha = 0.4) +
theme(legend.position = "none") +
labs(title = "number of fragments") +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none")
cowplot::plot_grid(p3, p2, p1, ncol = 3)

Plot Fragment Size Distribution & TSS Enrichment Profiles
- the distribution of fragments size can be very different between samples, cell types and batches -> these differences do not necessarily correlate with differences in quality
- the dip is the fragment size of a nucleosome ~147bp
- TSS enrichment profiles
- clear peak in the center
- smaller shoulder peak right of the center caused by well positioned +1 nucleosome
#{r, fig.width=5, fig.height=5} p1 <- plotFragmentSizes(ArchRProj = proj) p2 <- plotTSSEnrichment(ArchRProj = proj) ggAlignPlots(p1, p2, type = "v")
Filtering Doublets
In the plots below the Doublet Enrichment represents the enrichment of simulated dobulets nearby each cell, compared to the expected number of doublets if a uniform distriution is assumed.
The doublet density vissualizes where the synthetic doublets are located after projecting them into the 2D embedding.
# the doublet information is saved in a simpleListobject
dir <- "QualityControl/"
doublet_enrichment <- map(stages, function(n) {
path <- paste0(dir, n, "/")
all_files <- list.files(path)
file <- all_files[grepl(paste0(n, "-Doublet-Summary.rds"), all_files)]
# read in file containing doublet summary for sample n
doublet_summary <- readRDS(paste0(dir, n, "/", file))
doublet_summary[[1]] %>%
ggplot() +
geom_point(aes(x = X1, y = X2, col = enrichment), size = .8) +
scale_color_viridis_c() +
guides(fill=guide_legend(title="DoubletEnrichment")) +
labs(title = paste0("Simulated Doublet Enrichment", n))
})
gridExtra::grid.arrange(grobs = doublet_enrichment, ncol = 2)

doublet_density <- map(stages, function(n) {
path <- paste0(dir, n, "/")
all_files <- list.files(path)
file <- all_files[grepl(paste0(n, "-Doublet-Summary.rds"), all_files)]
# read in file containing doublet summary for sample n
doublet_summary <- readRDS(paste0(dir, n, "/", file))
doublet_summary[[2]] %>%
ggplot() +
geom_point(aes(x = x, y = y, col = density), size = .1) +
geom_point(data = doublet_summary[[1]], aes(x = X1, y = X2), size = .1,
alpha = .3) +
scale_color_viridis_c() +
guides(fill=guide_legend(title="Simulated Doublet Density")) +
labs(title = "Simulated doublet density overlayed")
})
gridExtra::grid.arrange(grobs = doublet_density, ncol = 2)

With the function addDoubleScores() information on predicted doublets has been added. Now we will filter the putative doublets. ArchR automatically prints the number of cells removed from each sample and the corresponding percentage.
arguments:
- cutEnrich = minimum cutoff for DoubletEnrichment, number of simulated doublets divided by expected number given a random uniform distribution
- cutScore = minimum cutoff for Doublet Score, represents -log10(binomial adjusted p-value) for the DoubletEnrichmentadd
- filterRatio = maximum ratio of predicted doublets to filter based on number of pass-filter cells (A higher filterRatio means that more cells are removed) e.g. 5000 cells
maximum would be filterRatio * 5000 / 100000 = filterRatio * 5000 * 0.05
This way samples with different percentage of doublets will be filtered accordingly.
As can be seen below, in E7.5_rep1 no doublets are filtered. Why might that be?
# in our case we now have 10 251 cells as opposed to 10 661 cells before
# filtering -> 410 cells were removed (3.85%)
proj <- filterDoublets(ArchRProj = proj)
Use already annotated cells
Subset cells
What is the overlap between my QC and the processed and annotated data?
In total after QC and doublet filtering the dataset contains 59,113 cells. First, we have to remove the cells from E8.75_rep1/2, because these samples are not included in the processed and annotated data. 8,880 cells are from samples E8.75_rep1/2. In the processed data matrix we find 38,158 cells. Only 30,803 cells pass RNA QC. Of the scRNA-seq dataset only 30,282 out of 36,822 cells pass the scATAC-seq QC. This means that we have 30,282 cells with annotated celltypes. These are the cells we want to use in the following analysis. We will, therefore, subset our dataset. We are now left with 28,154 cells for further analysis. Apparently we filtered some cells not fitlered in the processed data.
To visualize this in a better way create a Venn diagram.
# read in processed scATAC-seq data
proc_atac <- readH5AD("anndata_atac.h5ad")
# remove cells which do not pass the rnaQC
remove <- (as.data.frame(colData(proc_atac)) %>% rownames_to_column("cell") %>%
filter(pass_rnaQC == TRUE))[["cell"]]
filt_processed_atac <- proc_atac[ , colnames(proc_atac) %in% remove]
# check the RNA dataset as well to see if the cells overlap with the ATAC cells
proc_rna <- readH5AD("anndata_rna.h5ad")
## Warning in .extract_or_skip_assay(skip_assays = skip_assays, hdf5_backed =
## hdf5_backed, : 'spliced' layer matrix does not support transposition and has
## been skipped
## Warning in .extract_or_skip_assay(skip_assays = skip_assays, hdf5_backed =
## hdf5_backed, : 'unspliced' layer matrix does not support transposition and has
## been skipped
# keep the cells which pass both rnaQC and atacQC
keep <- colnames(proc_rna[,colnames(proc_rna) %in% colnames(proc_atac)])
# remove the samples E8.75
idxSample <- BiocGenerics::which(proj$Sample %in% c("E7.5_rep1", "E7.5_rep2",
"E8.0_rep1", "E8.0_rep2",
"E8.5_rep1", "E8.5_rep2"))
filt_proj <- proj[proj$cellNames[idxSample]]
In the following plot you can see the cells which passed quality control for scRNA-seq, scATAC-seq and which are included in the ArchR object with a very loose quality control. We will proceed only using the overlap between the three, because for the overlap the celltypes are annotated already. These are also the cells which are used as an input to scDori.
# plot the overlap between the different versions
p <- ggVennDiagram::ggVennDiagram(list(colnames(proc_atac), colnames(proc_rna), filt_proj$cellNames),
label_alpha = 0,
category.names= c("processed ATAC", "processed RNA", "ArchR project"),
#set.size = 20,
label = "count",
label_size = 6) +
scale_fill_distiller(palette = "RdBu") +
labs(title = "All TFs with different activity")
p
# keep only the cells which passed RNA QC and ATAC QC -> celltype annotations
overlap <- filt_proj$cellNames[filt_proj$cellNames %in% keep]
# filter the ArchR object
filt_proj <- filt_proj[overlap, ]
# subset both the processed atac data and rna data to get only the overlapping cells
rna_overlap <- proc_rna[, colnames(proc_rna) %in% overlap]
atac_overlap <- proc_atac[, colnames(proc_atac) %in% overlap]
Save the subset ArchR project and proceed with it for now. It might be interesting to revisit and see if I can cluster and identify the cell types de novo at a later stage!
Dimensionality reduction
- two other algorithms:
- latent semantic indexing (LSI) in Signac
- landmark diffusion maps (LDM) in SnapATAC
- ArchR: optimized iterative LSI method
- exhibits less susceptibility to batch effects
- focuses on most variable features
- create a LSI Reduction from a subset of the total cells
- linearly project the remaining cells into this subspace with LSI projection (based on SVD)
Because we can have maximally two accessible alleles per cell, the scATAC-seq data is sparse. Therefore, the majority of accessible regions are not transposed, meaning that most loci will have 0 accessible alleles. A zero could mean “non-accessible” or “not sampled”. For many analysis we can use a binarized matix. Imporantly, the 1s have information, BUT the 0s do not!
A PCA would result in high inter-cell similarity at all 0 positions. An alternative approach for dimensionality reduction is a layered dimensionality reduction. First, Latent Semantic Indexing (LSI) is used. LSI is an approach from language processing. Different samples are the “documents” and different regions/peaks are the “words”.
Iterative LSI
- compute term frequency (depth normalization to 10,000 per single cell)
\(TF = \frac{C_{ij}}{F_{j}}\), the term frequency of \(C_{ij}\) which is tiles i in cell j and \(F_{j}\) being the total number of tiles with insertions in cell j.
- Inverse document frequency
- weights features by how often they occur
- more weight to less frequent peaks
\(IDF_{i} =\log {\frac{N}{1 + DF_{i}}\) with N being the total number of cells in the dataset and \(DF_{i}\) being the total number of cells (documents) in which the tile i is present.
- The term frequency TF is normalized by the inverse document frequncy IDF. You get a TF-IDF matrix (term frequency-inverse document frequency) which tells us how important a region/peak is to a sample. In other words you transform a binary matrix to a non-binary matrix.
\(TF-IDF = \log{1 + TF{ij}} * IDF_{i} * 10^{4}}\)
- SVD identifies the most valuable information across samples. Then we can use these most valuable features to represent the data in a lower dimensional space
- Clusters are identified with Seurat’s Shared Nearest Neighbor clustering
- Sum accessibility across all single cells in each cluster -> log-normalize
- Identify most variable features across the clusters
- repeat with most variable peaks as features
With LSI we can reduce the dimensionality of the sparse insertion matrix to tens or hundreds. Then UMAP or t-SNE can be used to visualize the data
Unlike in scRNA-seq we cannot select the top highly variable features before dimensionality reduction (high noise, low reproducibility). Rather the iterative LSI approach first computes a LSI on the most accessible tiles (this will identify clusters corresponding to the major cell types). Then, ArchR computes the average accessibility across these clusters across all features. Next, the most variable peaks across these clusters are identified. The most highly accessible peaks are the features of a new round of LSI. We can set how many rounds of LSI we want to be peformed.
Using iterative LSI reduces batch effects. If you see some batch effects you could try to add more LSI iterations and start from a lower initial clustering resolution. Also, the number of variable features can be lowered. #
#filt_proj <- addIterativeLSI(ArchRProj = filt_proj, useMatrix = "TileMatrix",
# name = "atac_LSI_25000")
#filt_proj <- addIterativeLSI(ArchRProj = filt_proj, useMatrix = "TileMatrix",
# name = "atac_LSI_50000")
filt_proj <- addIterativeLSI(ArchRProj = filt_proj, useMatrix = "TileMatrix",
name = "atac_LSI_100000")
Clustering
Calling clusters in this new space uses the Seurat’s graph clustering function as default clustering method. The Seurat method first computs KNN graph and then a modularity optimization technique to cluster the cells (iteratively group cells together with Louvian algorithm using 10 random starts). Another option is to use “Scran”. The default number of nearest neighbors used is 10. The minimum number of cells for a cluster to be called a cluster is set to 5 by default. The maximum number of clusters to be called is set to 25 by default.
proj <- addClusters(input = proj, reducedDims = "IterativeLSI")
Visualization
We will add the celltype annotation from the processed data to our ArchR project. Since we already have annotations, we do not need to perform clustering.
#filt_proj <- addTSNE(ArchRProj =filt_proj, reducedDims = "atac_LSI_100000")
filt_proj <- addUMAP(ArchRProj =filt_proj, reducedDims = "atac_LSI_100000")
filt_proj <- loadArchRProject(path = "02_filtered_annotated_ArchRobject/")
# add celltype annotations form the processed atac to the ArchR object
# get vector of celltype annotations
celltypes <- as.character(atac_overlap$celltype.mapped)
# name celltype vector with cell names
names(celltypes) <- colnames(atac_overlap)
#filt_proj <- addCellColData(filt_proj,
# data = celltypes,
# name = "celltypes",
# cells = colnames(atac_overlap)
# )
# add custom colors
colPalette_celltypes = c('#532C8A',
'#c19f70',
'#f9decf',
'#c9a997',
'#B51D8D',
'#3F84AA',
'#9e6762',
'#354E23',
'#F397C0',
'#ff891c',
'#635547',
'#C72228',
'#f79083',
'#EF4E22',
'#989898',
'#7F6874',
'#8870ad',
'#647a4f',
'#EF5A9D',
'#FBBE92',
'#139992',
'#cc7818',
'#DFCDE4',
'#8EC792',
'#C594BF',
'#C3C388',
'#0F4A9C',
'#FACB12',
'#8DB5CE',
'#1A1A1A',
'#C9EBFB',
'#DABE99',
'#65A83E',
'#005579',
'#CDE088',
'#f7f79e',
'#F6BFCB')
celltypes <- (as.data.frame(getCellColData(filt_proj)) %>% group_by(celltypes) %>%
summarise(n = n()))$celltypes
col <- setNames(colPalette_celltypes, celltypes)
df <- as_data_frame(cbind(getCellColData(filt_proj), getEmbedding(filt_proj)) ) %>%
rename(c(umap1 = atac_LSI_100000.UMAP_Dimension_1, umap2 = atac_LSI_100000.UMAP_Dimension_2))
variables <- c("celltypes", "Sample", "nFrags", "DoubletScore")
plot1 <- map(c("celltypes"), function(n){
ggplot() +
geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"),
col = df %>% pull(n)), size = .8) +
guides(col=guide_legend(title=paste0(n))) +
scale_color_manual(values = col)+
xlab("umap1") +
ylab("umpa2") +
labs(title = paste0(n))
})
plots2 <- map(c("nFrags", "DoubletScore"), function(n){
ggplot() +
geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"),
col = df %>% pull(n)), size = .8) +
scale_color_viridis_c() +
guides(fill=guide_legend(title=paste0(n))) +
xlab("umap1") +
ylab("umpa2") +
labs(title = paste0(n))
})
plot3 <- map(c("Sample"), function(n){
ggplot() +
geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"),
col = df %>% pull(n)), size = .8) +
guides(col=guide_legend(title=paste0(n))) +
xlab("umap1") +
ylab("umpa2") +
labs(title = paste0(n))
})
plot1
## [[1]]

do.call(gridExtra::grid.arrange, c(plot3, plots2, ncol=3))#, nrow = 2))

#saveArchRProject(filt_proj, outputDirectory = "02_filtered_annotated_ArchRobject", load = FALSE)
Check gene scores for the different celltypes
filt_proj <- addImputeWeights(filt_proj, reducedDims = "atac_LSI_100000")
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
markerGenes <- c(
"Dnmt3b", # Epigblas
"Ptn", # Forebrain/Midbrain/Hindbrain
"Pou5f1", # Primitive Streak
"Myl7", # cardiomyocytes
"Hoxa10" #Allantois
)
#{r, fig.height=10, fig.width=10} p <- plotEmbedding( ArchRProj = filt_proj, colorBy = "GeneScoreMatrix", name = markerGenes, embedding = "UMAP", imputeWeights = getImputeWeights(proj) ) do.call(gridExtra::grid.arrange, c(p, ncol = 3))
Visualizing Genome Browser Tracks
Browse local chromatin accessibility at marker genes. Plot genome browser tracks per celltype
p <- plotBrowserTrack(
ArchRProj = filt_proj,
groupBy = "celltypes",
geneSymbol = markerGenes, # the plot window is centered at the TSS
upstream = 50000,
downstream = 50000
)
stages_plots <- plotBrowserTrack(
ArchRProj = filt_proj,
groupBy = "Sample",
geneSymbol = markerGenes,
upstream = 50000,
downstream = 50000
)
markerGenes <- c(
"Dnmt3b", # Epigblas
"Ptn", # Forebrain/Midbrain/Hindbrain
"Pou5f1", # Primitive Streak
"Myl7", # cardiomyocytes
"Hoxa10" #Allantois
)
- Dnmt3b = Epiblast
- Ptn = Forebrain/Midbrain/Hindbrain
- Pouf5f1 = Primitive Streak
- Myl7 = cardiomyocytes
- Hoxa10 = Allantois
Since we group the cells accoring to celltype below and plot the average signal/accessibility. Since some signals are only expected in a subset of the embryonic stages, the signals might be diluted if we look at all stages at once and take the mean accessibility over different stages. It would probabl
```#{r,fig.height=15, fig.width=10} for (elem in p){ grid::grid.newpage() grid::grid.draw(elem) }
```#{r}
for (elem in stages_plots){
grid::grid.newpage()
grid::grid.draw(elem)
}
Different Time points
To differentiate between expression at different stages we will have to split the ArchR projects into
Combine with RNA-seq data
Since we are using multiome data, we do not need to integrate the ATAC and RNA data, but we have both informations from the same cell. We will use the processed scRNA-seq data which we filtered to contain only the cells which overlap with the cells in the ArchR object.
Look here for more details on multiome analysis in ArchR: https://greenleaflab.github.io/ArchR_2020/Ex-Analyze-Multiome.html
One interesting thing to notice is that the number of genes in the gene score matrix is different from the number of genes in the RNA dataset.
I get an error when trying to extract the tile matrix. Not sure what is happening there.
# extract gene score matrix
gene_scores <- getMatrixFromProject(filt_proj, useMatrix = "GeneScoreMatrix")
gene_scores_mtx <- assays(gene_scores)[[1]]
# set names of the gene score matrix
rownames(gene_scores_mtx) <- rowData(gene_scores)$name
#tile_matrix <- getMatrixFromProject(filt_proj, useMatrix = "TileMatrix", binarize = TRUE)
print(paste0("The number of genes in the ATAC-seq Gene Score Matrix is: ",
nrow(gene_scores_mtx)))
## [1] "The number of genes in the ATAC-seq Gene Score Matrix is: 24333"
print(paste0("The number of genes in the RNA-seq Gene Expression Matrix is: ",
dim(rna_overlap)[1]))
## [1] "The number of genes in the RNA-seq Gene Expression Matrix is: 32245"
print(paste0("The number of genes found in both the Gene Expression Matrix and the Gene Score Matrix:",
length(intersect(rownames(gene_scores_mtx), rownames(rna_overlap)))))
## [1] "The number of genes found in both the Gene Expression Matrix and the Gene Score Matrix:22273"
Filter genes which are found in gene score matrix and gene expression matrix. Reorder cell names to match between gene scores and gene expression. Here it would be nice to show VennDiagrams to quickly be able to understand what the differences in genes are between ArchR object and gene expression matrix. Is there a way to include also genes which do not overlap?
# Try to reorder the cellnames to the
# this is the order of the cell names in the ArchR project gene score matrix
order <- rownames(getCellColData(filt_proj))
# extract the gene expression matrix
rna_counts <- assays(rna_overlap)[[1]]
# reorder gene expression matrix cell names to match the cellnames of the ArchR object
rna_counts <- rna_counts[, order]
# I checked that they are the same
#colnames(rna_counts) == rownames(getCellColData(filt_proj))
# get the gene names of the gene score matrix
genes <- rownames(gene_scores_mtx)
# subset the gene expression matrix to obtain only the genes found in the gene
# score matrix
rna_counts <- rna_counts[rownames(rna_counts) %in% genes,]
Next we have to create a GRanges object which will be used in the next step to create a RangedSummarizedExperiment. When using the gene annotation metadata stored in the rowData of the RNA-SCE for creating this RangedSummarizedExperiment with the ArchRobject, I got an error message. This might have been due to the fact that the gene annotations looked different between the RNA-SCE and the ArchR object. Therefore, I tried using the same gene annotation as ArchR uses. In order for this to work, we will have to exclude any genes which are not used by ArchR.
# get the Granges object from the Archr project
archr_granges <-getGenes(filt_proj)
# filter for genes which are included in the gene expression matrix
granges <- subset(archr_granges, symbol %in% rownames(rna_counts))
# the problem here was that the gene annotations of the expression matrix
did not match the gene annotations of the Archr object.
df <- rowData(rna_overlap) %>% as.data.frame() %>% filter(gene %in%genes)
df
granges <- df %>% GRanges()
granges
We will now add the gene expression matrix to the ArchR object. First, we have to convert the Single Cell Experiment to a Summarized Experiment.
# create summarized experiment of the scRNA-seq
seRNA <- SummarizedExperiment(assays = list(counts=rna_counts),
colData = colnames(rna_overlap),
rowRanges = granges)
seRNA
## class: RangedSummarizedExperiment
## dim: 22273 28154
## metadata(0):
## assays(1): counts
## rownames(22273): Xkr4 Rp1 ... Asmt Ccl21c
## rowData names(2): gene_id symbol
## colnames(28154): E7.5_rep2#TGTAATGTCATGCTAA-1
## E7.5_rep2#GCGCTTAAGCATTTCT-1 ... E8.0_rep2#CGCCTGTGTACTGAAT-1
## E8.0_rep2#TTGTGAGGTTTAAAGC-1
## colData names(1): X
# add RNA gene expression matrix to the ArchR object
filt_proj <- addGeneExpressionMatrix(input = filt_proj, seRNA = seRNA, force = TRUE)
# save the ArchR object with the added gene expression matrix
#saveArchRProject(filt_proj, outputDirectory = "03_added_gene_expression_ArchRobject", load = FALSE)
We will add a second LSI for the RNA data. The one based on the ATAC data is called “LSI_ATAC”. We will call the one for the RNA data “LSI_RNA”
filt_proj <- addIterativeLSI(
ArchRProj = filt_proj,
clusterParams = list(
resolution = 0.2,
sampleCells = 10000,
n.start = 10
),
saveIterations = FALSE,
useMatrix = "GeneExpressionMatrix",
depthCol = "Gex_nUMI",
varFeatures = 2500,
firstSelection = "variable",
binarize = FALSE,
name = "LSI_RNA"
)
filt_proj <- addIterativeLSI(
ArchRProj = filt_proj,
saveIterations = FALSE,
clusterParams = list(
resolution = 0.2,
sampleCells = 10000,
n.start = 10
),
useMatrix = "GeneExpressionMatrix",
depthCol = "Gex_nUMI",
varFeatures = 100000,
firstSelection = "Var",
binarize = FALSE,
name = "LSI_RNA"
)
Now we will combine the two embeddings.
#Combined Dims
proj <- addCombinedDims(proj, reducedDims = c("LSI_ATAC", "LSI_RNA"), name = "LSI_Combined")
#UMAPs
proj <- addUMAP(proj, reducedDims = "LSI_ATAC", name = "UMAP_ATAC", minDist = 0.8, force = TRUE)
proj <- addUMAP(proj, reducedDims = "LSI_RNA", name = "UMAP_RNA", minDist = 0.8, force = TRUE)
proj <- addUMAP(proj, reducedDims = "LSI_Combined", name = "UMAP_Combined", minDist = 0.8, force = TRUE)
LS0tCnRpdGxlOiAiTW91c2UgR2FzdHJ1bGF0aW9uIHNjQVRBQy1zZXEgJiBzY1JOQS1zZXEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA1CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlCi0tLQoKPHN0eWxlPgpib2R5IHsKdGV4dC1hbGlnbjoganVzdGlmeX0KPC9zdHlsZT4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBGQUxTRSwgYXV0b2RlcCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvIikKc2V0d2QoIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvIikKCnNldC5zZWVkKDEpCmBgYAoKICAKYGBge3J9CiMjIExvYWQgbGlicmFyaWVzCgoKbGlicmFyeShBcmNoUikKbGlicmFyeShrbml0cikKbGlicmFyeShyaGRmNSkKbGlicmFyeSh1d290KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzY2F0ZXIpCmxpYnJhcnkoemVsbGtvbnZlcnRlcikKI2xpYnJhcnkoY2FyZXQpCmg1ZGlzYWJsZUZpbGVMb2NraW5nKCkKYGBgCiAgCiAgClRoZSBzY0FUQUMtc2VxIGRhdGFzZXQgY29udGFpbnMgdHdvIHJlcGxpY2F0ZXMgZWFjaCBmcm9tIDQgc3RhZ2VzIG9mIG1vdXNlIApnYXN0cnVsYXRpb24sIEU3LjUsIEU4LjAsIEU4LjUgYW5kIEU4Ljc1LiBXZSB3aWxsIHVzZSB0aGUgbW0xMCBHZW5vbWUsIGJlY2F1c2UgCnRoYXQgaXMgdGhlIGdlbm9tZS4KCmBgYHtyfQojIG5hbWVzIG9mIGdhc3RydWxhdGlvbiBzdGFnZXMKc3RhZ2VzIDwtIGMoIkU3LjVfcmVwMSIsICJFNy41X3JlcDIiLCAiRTguMF9yZXAxIiwgIkU4LjBfcmVwMiIsIAogIkU4LjVfcmVwMSIsICJFOC41X3JlcDIiLCAiRTguNzVfcmVwMSIsICJFOC43NV9yZXAyIikKCiMgY29tYmluZSBhbGwgcmF3IGZyYWdtZW50IGZpbGVzCmlucHV0RmlsZXMgPC0gc2V0TmFtZXMoYygicmF3X2ZpbGVzL0U3LjVfcmVwMV9hdGFjX2ZyYWdtZW50cy50c3YuZ3oiLAogICAgICAgICAgICAgICAgICAgICAgICJyYXdfZmlsZXMvRTcuNV9yZXAyX2F0YWNfZnJhZ21lbnRzLnRzdi5neiIsCiAgICAgICAgICAgICAgICAgICAgICAgInJhd19maWxlcy9FOC4wX3JlcDFfYXRhY19mcmFnbWVudHMudHN2Lmd6IiwKICAgICAgICAgICAgICAgICAgICAgICAicmF3X2ZpbGVzL0U4LjBfcmVwMl9hdGFjX2ZyYWdtZW50cy50c3YuZ3oiLAogICAgICAgICAgICAgICAgICAgICAgICJyYXdfZmlsZXMvRTguNV9yZXAxX2F0YWNfZnJhZ21lbnRzLnRzdi5neiIsCiAgICAgICAgICAgICAgICAgICAgICAgInJhd19maWxlcy9FOC41X3JlcDJfYXRhY19mcmFnbWVudHMudHN2Lmd6IiwKICAgICAgICAgICAgICAgICAgICAgICAicmF3X2ZpbGVzL0U4Ljc1X3JlcDFfYXRhY19mcmFnbWVudHMudHN2Lmd6IiwKICAgICAgICAgICAgICAgICAgICAgICAicmF3X2ZpbGVzL0U4Ljc1X3JlcDJfYXRhY19mcmFnbWVudHMudHN2Lmd6IiksCiAgICAgICAgICAgICAgICAgICAgICAgYygiRTcuNV9yZXAxIiwgIkU3LjVfcmVwMiIsICJFOC4wX3JlcDEiLCAiRTguMF9yZXAyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiRTguNV9yZXAxIiwgIkU4LjVfcmVwMiIsICJFOC43NV9yZXAxIiwgIkU4Ljc1X3JlcDIiKSkKYGBgCgoKCgpgYGB7cn0KYWRkQXJjaFJHZW5vbWUoIm1tMTAiKQpgYGAKCiMjIENyZWF0ZSBBcnJvdyBGaWxlcwoKV2Ugd2lsbCByZWFkIGluIHRoZSBhY2Nlc3NpYmxlIGZyYWdtZW50cyBmcm9tIHRoZSBmcmFnbWVudHMgZmlsZS4gUUMgaW5mb3JtYXRpb24KaXMgYXV0b21hdGljYWxseSBjb21wdXRlZCBmb3IgZWFjaCBjZWxsLiBCYXNlZCBvbiB2aXN1YWwgaW5zcGVjdGlvbiBvZiBUU1MgRW5yaWNobWVudApzY29yZSB2cyBsb2cxMChuRnJhZ3MpIEkgdXNlZCBhIHRocmVzaG9sZCBvZiBhdCBsZWFzdCAxMDAwIHVuaXF1ZSBmcmFnbWVudHMgYW5kCmEgVFNTIEVucmljaG1lbnQgc2NvcmUgb2YgYXQgbGVhc3QgMi43NS4gQXQgdGhlIGJlZ2lubmluZyBpdCBpcyBiZXR0ZXIgdG8gYmUgbGVzcwpzdHJpbmdlbnQsIGJlY2F1c2Ugd2UgY2FuIHN0aWxsIGZpbHRlciB0aGUgZGF0YSBhdCBhIGxhdGVyIHN0YWdlLiBUaGUgY2VsbHMgYXJlIApmaWx0ZXJlZCBiYXNlZCBvbiB0aGVzZSB0aHJlc2hvbGQgYW5kIGRhdGEgYXJlIHN0b3JlZCBpbiBhcnJvdyBmaWxlcy4gR2Vub21lLXdpZGUKdGlsZSBtYXRyaWNlcyBhcmUgY3JlYXRlZCB1c2luZyA1MDAtYnAgYmlucy4gVGhlIHRpbGUgbWF0cml4IFQgaXMgYmluYXJ5IG1hdHJpeCAKYW5kIGNvbnRhaW5zIGluZm9ybWF0aW9uIHdoZXRoZXIgaW4gYSBwYXJ0aWN1bGFyIDUwMC1icCBiaW4vdGlsZSBpIHRoZXJlIHdlcmUgYW55IGluc2VydGlvbnMgaW4gYQpwYXJ0aWN1bGFyIGNlbGwgai4KCiRUIFxpbiBcbWF0aGJie1J9XntOWER9JCB3aXRoICRUX3tpLGp9ID0gMSQgaWYgYW4gaW5zZXJ0aW9uIGlzIHByZXNlbnQgaW4gdGlsZSAkaSQKYW5kIGNlbGwgJGokLiAkVF97aSxqfSA9IDAkIGlmIG5vIGluc2VydGlvbiBpcyBwcmVzZW50IGluIHRoYXQgY2VsbCwgbWVhbmluZyAkVCQgCmlzIGEgYmluYXJ5IG1hdHJpeC4KCkluIGFkZGl0aW9uIGEgR2VuZVNjb3JlTWF0cml4IGlzIGNyZWF0ZWQuIAoKSSBzZXQgdGhlIHNpemUgb2YgdGhlIG1heGltdW0gbnVtYmVyIG9mIGZyYWdtZW50cyBpbmNsdWRlZCB0byAxZSswNywgaW5zdGVhZApvZiB0aGUgZGVmYXVsdCAxZSswNSwgYmVjYXVzZSB3ZSBjdXQgb2ZmIHNvbWUgY2VsbHMgb3RoZXJ3aXNlLgoKYGBgI3tyfQpBcnJvd0ZpbGVzIDwtIGNyZWF0ZUFycm93RmlsZXMoCiAgaW5wdXRGaWxlcyA9IGlucHV0RmlsZXMsCiAgc2FtcGxlTmFtZXMgPSBuYW1lcyhpbnB1dEZpbGVzKSwKICBtaW5UU1MgPTIuNzUsICNEb250IHNldCB0aGlzIHRvbyBoaWdoIGJlY2F1c2UgeW91IGNhbiBhbHdheXMgaW5jcmVhc2UgbGF0ZXIKICBtaW5GcmFncyA9IDEwMDAsICMgbWluaW11bSBudW1iZXIgb2YgbWFwcGVkIGZyYWdtZW50cyByZXF1aXJlZAogICMgY291bnQgbWF0cml4LCBpbnN0ZWFkIG9mIHVzaW5nIHBlYWsgaXQgdXNlcyBmaXhlZC13aWR0aCBzbGlkaW5nIHdpbmRvdyBvZiBiaW5zIGFjcm9zcyB0aGUgd2hvbGUgZ2Vub21lCiAgYWRkVGlsZU1hdCA9IFRSVUUsIAogIGFkZEdlbmVTY29yZU1hdCA9IFRSVUUsICMgdXNlcyBzaWduYWwgcHJveGltYWwgdG8gdGhlIFRTUyB0byBlc3RpbWF0ZSBnZW5lIGFjdGl2aXR5CiAgc3ViVGhyZWFkaW5nID0gRkFMU0UsCiAgbWF4RnJhZ3MgPSAxZSswNywKICBtaW5GcmFnU2l6ZSA9IDEwLAogIG1heEZyYWdTaXplID0gMjAwMCwKICBRQ0RpciA9ICJRdWFsaXR5Q29udHJvbCIsCiAgZm9yY2UgPSBGQUxTRSwgIyBjaGFuZ2UgdG8gVFJVRSBpZiB5b3Ugd2FudCB0byBvdmV3cml0ZSB0aGUgZmlsZXMKICAjIHRoZSBsZW5ndGggaW4gYnAgdGhhdCB3cmFwcyBhcm91bmQgbnVjbGVvc29tZXMgLT4gCiAgICAjaWRlbnRpZnkgZnJhZ21lbnRzIGFzIHN1Yi1udWNsZW9zb21lIHNwYW5uaW5nLCBtb25vLW51Y2xlb3NvbWUgc3Bhbm5pbmcgb3IgbXVsdGktbnVjbGVvc29tZSBzcGFubmluZwogIG51Y0xlbmd0aCA9IDE0NywgCiAgIyBpbnRlZ2VyIHZlY3RvciAtPiBkZWZpbmUgcmVnaW9uIHVwL2Rvd25zdHJlYW0gb2YgVFNTIHRvIGluY2x1ZGUgYXMgcHJvbW90ZXIgcmVnaW9uCiAgIyBjYW4gYmUgdXNlZCB0byBjYWxjdWxhdGUgZS5nIGZyYWN0aW9uIG9mIHJlYWRzIGluIHByb21vdGVyIChGSVApCiAgcHJvbW90ZXJSZWdpb24gPSBjKDIwMDAsIDEwMCksCiAgIyBwYXJhbWV0ZXJzIGZvciBjb21wdXRpbmcgVFNTIGVucmljaG1lbnQgc2NvcmVzLCB3aW5kb3cgKGJwKSBjZW50ZXJlZCBhdCBUU1MgPSAxMDEKICAjIGZsYW5raW5nIHdpbmRvdyA9IDIwMDAgYnAKICAjIG5vcm0gPSBzaXplIG9mIGZsYW5rIHdpbmRvdyB1c2VkIGZvciBub3JtYWxpemF0aW9uID0gMTAwIGJwCiAgIyBhY2Nlc3NpYmlsaXR5IHdpdGhpbiAxMDEgYnAgc3Vycm91bmRpbmcgdGhlIFRTUyB3aWxsIGJlIG5vcm1hbGl6ZWQgdG8gYWNjZXNzaWJpbGl0eQogICMgaW4gMTAwIGJwIGJpbnMgZnJvbSAtMjAwMDotMTkwMWJwIGFuZCAxOTAxOiAyMDAwCiAgVFNTUGFyYW1zID0gbGlzdCgxMDEsIDIwMDAsIDEwMCksCiAgIyB3aGljaCBjaHJvbW9zb21lcyB0byBleGNsdWRlIGZvcm0gZG93bnN0cmVhbSBhbmFseXNpcwogICMgaW4gaHVtYW4gYW5kIG1vdXNlOiBtaXRvY2hvbmRyaWFsIEROQSAoY2hyTSkgYW5kIG1hbGUgc2V4IGNocm9tb3NvbWUgKGNoclkpCiAgIyB0aGUgZnJhZ21lbnRzIGFyZSBzdGlsbCBzdG9yZWQgaW4gdGhlIGFycm93IGZpbGVzCiAgZXhjbHVkZUNociA9IGMoImNock0iLCAiY2hyWSIpLAogICMgbnVtYmVyIG9mIGNodW5rcyB0byBkaXZpZGUgY2hyb21vc29tZXMgaW4gLT4gbG93LW1lbW9yeSBwYXJhbGxlbGl6ZWQgcmVhZGluZyBvZiBpbnB1dCBmaWxlcwogIG5DaHVuayA9IDUsCiAgIyBuYW1lIG9mIGZpZWxkIGluIGlucHV0IGJhbSBmaWxlIGNvbnRhaW5pbmcgdGhlIGJhcmNvZGUgdGFnIGluZm9ybWF0aW9uCiAgYmNUYWcgPSAicW5hbWUiLAogIG9mZnNldFBsdXMgPSA0LCAjIG9mZnNldCBhcHBsaWVkIHRvICsgc3RyYW5kZWQgVG41IGluc2VydGlvbiAtPiBhY2NvdW50IGZvciBwcmVjaXNlIFRuNSBiaW5kaW5nIHNpdGUKICBvZmZzZXRNaW51cyA9IC01LCAKICBsb2dGaWxlID0gY3JlYXRlTG9nRmlsZSgiY3JlYXRlQXJyb3dzIikKKQpgYGAKCgoKCiMjIEluZmVycmluZyBEb3VibGV0cwoKU2hvdWxkIGJlIHJlbW92ZWQsIGJlY2F1c2UgdGhleSBjYW4gaW50ZXJmZXJlIHdpdGggZG93bnN0cmVhbSBhbmFseXNpcy4KCioqRG91YmxldCBkZXRlY3Rpb24tYW5kLXJlbW92YWwgYWxnb3JpdGhtOioqCkhldGVyb3R5cGljIGRvdWJsZXRzIGFyZSBpZGVudGlmaWVkIGJ5IGdlbmVyYXRpbmcgYSBjb2xsZWN0aW9uIG9mIHN5bnRoZXRpYyBkb3VibGV0cy4KVGhlc2Ugc3ludGhldGljIGRvdWJsZXRzIGFyZSBwcm9qZWN0ZWQgaW50byBsb3ctZGltZW5zaW9uYWwgZW1iZWRkaW5ncy4gU2VhcmNoaW5nCmZvciBuZWFyZXN0IG5laWdoYm91cnMgdG8gdGhlIHN5bnRoZXRpYyBkb3VibGV0cyB3ZSBjYW4gaWRlbnRpZnkgZG91YmxldHMgaW4gdGhlCmRhdGFzZXQuIFRoaXMgb3V0cGVyZm9ybXMgdGhlIHByZWRpY3Rpb24gb2YgZG91YmxldHMgdXNpbmcgZnJhZ21lbnQgbnVtYmVyCihST0MtQVVDKS4gKENvbXBhcmVkIHRvIGRlbXV4bGV0IGFzIGdyb3VuZCB0cnV0aCkKCioqV2UgY2FuIGFsc28gaWRlbnRpZnkgZG91YmxldHMgaW4gdGhlIHNjUk5BLXNlcSBzcGFjZSBpZiB3ZSBoYXZlIHBhaXJlZCBkYXRhCmFuZCByZW1vdmUgdGhlIGNlbGxzIGluIHRoaXMgd2F5LioqCgpgYGAje3J9CiMgZm9yIGVhY2ggc2FtcGxlIHByb3ZpZGVkIGRvdWJsZXQgaW5mb3JtYXRpb24gd2lsbCBiZSBhc3NpZ25lZCB0byBlYWNoIGNlbGwKIyB0aGlzIHdheSB3ZSBjYW4gcmVtb3ZlIGRvdWJsZXQtYmFzZWQgY2x1c3RlcnMgZG93bnN0cmVhbQpkb3ViU2NvcmVzIDwtIGFkZERvdWJsZXRTY29yZXMoCiAgdXNlTWF0cml4ID0gIlRpbGVNYXRyaXgiLAogIGlucHV0ID0gQXJyb3dGaWxlcywKICBrID0gMTAsICNSZWZlcnMgdG8gaG93IG1hbnkgY2VsbHMgbmVhciBhICJwc2V1ZG8tZG91YmxldCIgdG8gY291bnQuCiAgblRyaWFsID0gNSwgIyBudW1iZXIgb2YgdGltZSB0byBzaW11bGF0ZSBuQ2VsbCBkb3VibGV0cyAKICBrbm5NZXRob2QgPSAiVU1BUCIsICNSZWZlcnMgdG8gdGhlIGRpbWVuc2lvbmFsaXR5IHJlZHVjaXRvbiBtZXRob2QgdG8gdXNlIGZvciBuZWFyZXN0IG5laWdoYm9yIHNlYXJjaC4KICBMU0lNZXRob2QgPSAxICMgb2RlciBvZiBub3JtYWxpemF0aW9uOiB0Zi1sb2coaWRmKQopCmBgYAoKCgojIyBDcmVhdGUgQXJjaFJQcm9qZWN0CgpBbiBBcmNoUiBQcm9qZWN0IGlzIGluaXRpYWxpemVkIHdpdGggc29tZSBpbXBvcnRhbnQgYXR0cmlidXRlczoKCiogb3VwdXQgZGlyZWN0b3J5Ciogc2FtcGxlIG5hbWVzCiogYHNhbXBsZUNvbERhdGFgIC0+IG1hdHJpeCBjb250YWluaW5nIGRhdGEgZm9yIGVhY2ggc2FtcGxlCiogYGNlbGxDb2xEYXRhYCAtPiBjb250YWlucyBkYXRhIGFzc29jaWF0ZWQgd2l0aCBlYWNoIGNlbGwKICArIGFmdGVyIHVzaW5nIGBhZGREb3VibGV0U2NvcmUoKWAgdGhlcmUgd2lsbCBiZSBhIGNvbHVtbiAKICBmb3IgIkRvdWJsZXQgRW5yaWNobWVudCIgYW5kICJEb3VibGV0IFNjb3JlIgoqIHRvdGFsIG51bWJlciBvZiBjZWxscyAoZXhjbHVkaW5nIGRvdWJsZXRzKQoqIG1lZGlhbiBUU1Mgc2NvcmUgJiBtZWRpYW4gbnVtYmVyIG9mIGZyYWdtZW50cyBhY3Jvc3MgYWxsIGNlbGxzIAphbmQgc2FtcGxlcwoKYGBgI3tyfQpwcm9qIDwtIEFyY2hSUHJvamVjdCgKICBBcnJvd0ZpbGVzID0gQXJyb3dGaWxlcywgCiAgb3V0cHV0RGlyZWN0b3J5ID0gImdhc3RyQXJjaFJPdXRwdXQiLAogIGNvcHlBcnJvd3MgPSBUUlVFLCAjVGhpcyBpcyByZWNvbW1lbmVkIHNvIHRoYXQgeW91IG1haW50YWluIGFuIHVuYWx0ZXJlZCBjb3B5IGZvciBsYXRlciB1c2FnZS4KICBnZW5lQW5ub3RhdGlvbiA9IGdldEdlbmVBbm5vdGF0aW9uKCksCiAgI2dlbm9tZUFubm90YXRpb24gPSBnZXRHZW5lQW5ub3RhdGlvbigpLAogIHNob3dMb2dvID0gRkFMU0UKKQoKcHJvagpgYGAKCmBgYHtyfQojIHJlYWQgaW4gZXhpc3RpbmcgcHJvamVjdCwgc28gd2UgZG9uJ3QgaGF2ZSB0byByZXJ1biB0aGUgY3JlYXRpb24KI3NhdmVBcmNoUlByb2plY3QocHJvaiwgb3V0cHV0RGlyZWN0b3J5ID0gIjAxX0ZpcnN0UUNfQXJjaFJvYmplY3QvIiwgbG9hZCA9IEZBTFNFKQoKcHJvaiA8LSBsb2FkQXJjaFJQcm9qZWN0KHBhdGggPSAiMDFfRmlyc3RRQ19BcmNoUm9iamVjdC8iKQpgYGAKCgoKIyBRdWFsaXR5IENvbnRyb2wKCkhhdmUgYSBsb29rIGF0IHRoaXMgZm9yIGFkZGl0aW9uYWwgUUMhCmh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9BVEFDc2VxUUMvaW5zdC9kb2MvQVRBQ3NlcVFDLmh0bWwKCjEuIHRoZSBudW1iZXIgb2YgKip1bmlxdWUgbnVjbGVhciBmcmFnbWVudHMqKiAoYXMgb3Bwb3NlZCB0byBtaXRvY2hvbmRyaWFsIGZyYWdtZW50cykKQSBjZWxsIHdpdGggdmVyeSBmZXcgdXNhYmxlIGZyYWdtZW50cyB3aWxsIG5vdCBwcm92aWRlIGVub3VnaCBkYXRhIHRvIG1ha2UgdXNlZnVsIGNvbmNsdXNpb25zLgoyLiAqKnNpZ25hbC10by1iYWNrZ3JvdW5kIHJhdGlvKio6IGlmIHRoaXMgaXMgbG93IHRoaXMgcHJvYmFibHkgY29ycmVzcG9uZHMgdG8gZHlpbmcKY2VsbHMgd2hlcmUgdGhlIGVudGlyZSBnZW5vbWUgYWxsb3dzIHJhbmRvbSB0cmFuc3Bvc2l0aW9uCjMuICoqZnJhZ21lbnQgc2l6ZSBkaXN0cmlidXRpb24qKjogc2luY2UgMTQ3IGJwIGFyZSB3cmFwcGVkIGFyb3VuZCBhIG51Y2xlb3NvbWUgaXQgaXMgCmV4cGVjdGVkIHRoYXQgdGhlcmUgYXJlIGRlcGxldGlvbnMgb2YgZnJhZ21lbnRzIG9mIHRoaXMgbGVuZ3RoIGF0IHJlZ3VsYXIgaW50ZXJ2YWxzLiAKV2UgZXhwZWN0IHRvIHNlZSBhIHBlcmlvZGljIGRpc3RyaWJ1dGlvbiBvZiBmcmFnbWV0biBzaXplIGNvcnJlc3BvbmRpbmcgdG8gbnVjbGVvc29tZXMKKG1vbm8sIGRpLCB0cmksIC4uLiksIGJlY2F1c2UgVG41IGNhbm5vdCBjdXQgRE5BIHRoYXQgaXMgdGlnaHRseSB3cmFwcGVkIGFyb3VuZCAKYSBudWNsZW9zb21lLiAKCkRhdGEgYmVmb3JlIFFDIGFuZCBjb3JyZXNwb25kaW5nIHBsb3RzIGFyZSBzYXZlZCBpbiB0aGUgUXVhbGl0eSBDb250cm9sIG91dHB1dApmb2xkZXIuCgojIyBsb2cxMCh1bmlxdWUgZnJhZ21lbnRzKSB2cyBUU1MgZW5yaWNobWVudAoKIyMjIGJlZm9yZSBRQwoKV2UgdXNlZCBhIFRTUyBlbnJpY2htZW50IHNjb3JlIG9mID4gMi43NSBhbmQgYSBudW1iZXIgb2YgdW5pcXVlIGZyYWdtZW50cyA+IDEwMDAgKGxvZzEwKDEwMDApID0gMykuIApJdCBtaWdodCBtYWtlIHNlbnNlIHRvIHVzZSBkaWZmZXJlbnQgdGhyZXNob2xkcyBmb3IgZGlmZmVyZW50IHNhbXBsZXMuCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpkaXIgPC0gIlF1YWxpdHlDb250cm9sLyIKI2FsbF9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGgpCgoKYmVmb3JlUUMgPC0gbWFwKHN0YWdlcywgZnVuY3Rpb24obikgewogIHBhdGggPC0gcGFzdGUwKGRpciwgbiwgIi8iKQogIGFsbF9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGgpCiAgZmlsZSA8LSBhbGxfZmlsZXNbZ3JlcGwocGFzdGUwKG4sICItUHJlLUZpbHRlci1NZXRhZGF0YS5yZHMiKSwgYWxsX2ZpbGVzKV0KICAjIHJlYWQgaW4gZmlsZSBmb3Igc2FtcGxlIG4gY29udGFpbmluZyBvcmlnaW5hbCBkYXRhIGJlZm9yZSBRQwogIGRmIDwtIGFzLmRhdGEuZnJhbWUocmVhZFJEUyhwYXN0ZTAoZGlyLCBuLCAiLyIsIGZpbGUpKSkKICBkZiAlPiUgbXV0YXRlKFNhbXBsZSA9IG4pICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGxvZzEwKG5GcmFncyksIHkgPSBUU1NFbnJpY2htZW50KSkgKwogICAgZ2VvbV9kZW5zaXR5MmRfZmlsbGVkKCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMi43NSwgY29sb3I9ImdyZWVuIiwgbGluZXR5cGU9ImRhc2hlZCIpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGxvZzEwKDEwMDApLCBjb2xvcj0iZ3JlZW4iLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogICAgI2dlb21feHNpZGVkZW5zaXR5KGFlcyh4PWxvZzEwKHByZV9maWx0ZXJfbWV0YSRuRnJhZ3MpKSkgKwogICAgI2dlb21feXNpZGVkZW5zaXR5KGFlcyh5ID0gcHJlX2ZpbHRlcl9tZXRhJFRTU0VucmljaG1lbnQpKSArCiAgICB5bGltKGMoMi41LCA0LjUpKSArCiAgICBmYWNldF93cmFwKH5TYW1wbGUpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgbGFicyh4ID0gIkxvZzEwIFVuaXF1ZSBGcmFnbWVudHMiLCB5ID0gIlRTUyBFbnJpY2htZW50IFNjb3JlIikKfSkKICAKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBiZWZvcmVRQywgbmNvbCA9IDIpCgoKCmBgYAoKCgojIyMgYWZ0ZXIgUUMKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQptZXRhZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKQptZXRhZGF0YSAlPiUgaGVhZAoKbWV0YWRhdGEgJT4lIGdyb3VwX2J5KFNhbXBsZSkgJT4lIHN1bW1hcmlzZShuID0gbigpKQoKbWV0YWRhdGEgJT4lIGdncGxvdCgpICsKICBnZW9tX2RlbnNpdHkyZF9maWxsZWQoYWVzKHggPSBsb2cxMChuRnJhZ3MpLCB5ID0gVFNTRW5yaWNobWVudCkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAyLjc1LCBjb2xvcj0iZ3JlZW4iLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGxvZzEwKDEwMDApLCBjb2xvcj0iZ3JlZW4iLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogICNnZW9tX3hzaWRlZGVuc2l0eShhZXMoeD1sb2cxMChwcmVfZmlsdGVyX21ldGEkbkZyYWdzKSkpICsKICAjZ2VvbV95c2lkZWRlbnNpdHkoYWVzKHkgPSBwcmVfZmlsdGVyX21ldGEkVFNTRW5yaWNobWVudCkpICsKICB5bGltKGMoMi41LCA0LjUpKSArCiAgZmFjZXRfd3JhcCh+U2FtcGxlKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyh4ID0gIkxvZzEwIFVuaXF1ZSBGcmFnbWVudHMiLCB5ID0gIlRTUyBFbnJpY2htZW50IFNjb3JlIikKCmBgYAoKIyMjIEFyY2hSIHBsb3RzCgpgYGAje3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTE1fQojY3JlYXRlIDMgc2VwYXJhdGUgZGF0YWZyYW1lcyBmb3IgYWxsIHNhbXBsZXMKc2FtcGxlcyA8LSBtYXAodW5pcXVlKHByb2okU2FtcGxlKSwgZnVuY3Rpb24obmFtZSl7CiAgaW5kZXggPC0gQmlvY0dlbmVyaWNzOjp3aGljaChwcm9qJFNhbXBsZSAlaW4lIG5hbWUpCiAgY2VsbHMgPC0gcHJvaiRjZWxsTmFtZXNbaW5kZXhdCiAgc2FtcGxlX3N1YnNldCA8LSBwcm9qW2NlbGxzXQogIGRmIDwtIGdldENlbGxDb2xEYXRhKHNhbXBsZV9zdWJzZXQsIHNlbGVjdCA9IGMoImxvZzEwKG5GcmFncykiLCAiVFNTRW5yaWNobWVudCIpKQogIHAgPC0gZ2dQb2ludCgKICAgIHggPSBkZlssIDFdLCB5ID0gZGZbLCAyXSwgCiAgICBjb2xvckRlbnNpdHkgPSBUUlVFLCAjIHNob3VsZCB0aGUgZGVuc2l0eSBvZiBwb2ludHMgb24gdGhlIHBsb3QgYmUgaW5kaWNhdGVkIGJ5IGNvbG9yPwogICAgY29udGludW91c1NldCA9ICJzYW1iYU5pZ2h0IiwgCiAgICB4bGFiZWwgPSAiTG9nMTAgdW5pcXVlIGZyYWdtZW50cyIsCiAgICB5bGFiZWwgPSAiVFNTIGVucmljaG1lbnQiLAogICAgdGl0bGUgPSBwYXN0ZTAoIlNhbXBsZTogIiwgbmFtZSksCiAgICAjeGxpbSA9IGMobG9nMTAoNTAwKSwgcXVhbnRpbGUoZGZbLDFdLCBwcm9icyA9IDAuOTkpKSwKICAgICN5bGltID0gYygwLCBxdWFudGlsZShkZlssMl0sIHByb2JzID0gMC45OSkpCiAgICApICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMi43NSwgbHR5ID0gImRhc2hlZCIpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDMsIGx0eSA9ICJkYXNoZWQiKQp9KQoKI2RvLmNhbGwoImdyaWQuYXJyYW5nZSIsIGMoc2FtcGxlcywgbmNvbD00KSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gc2FtcGxlcywgbmNvbCA9IDIpCgoKCgpgYGAKCgoKCiMjIyBQbG90dGluZyBzYW1wbGUgc3RhdGlzdGljcwoKKiB3aGVuIHdlIGhhdmUgZGlzdGluY3Qgc2FtcGxlcywgaXQgY2FuIGJlIGltcG9ydGFudCB0byBjb21wYXJlIHZhcmlvdXMKbWV0cmljIGJldHdlZW4gc2FtcGxlcwoqIHJpZGdlIHBsb3RzICYgdmlvbGluIHBsb3RzIGFyZSB1c2VkIGZvciBncm91cGVkIGRhdGEKCmBgYHtyLGZpZy53aWR0aD0xNX0KCnAxIDwtIGFzX2RhdGFfZnJhbWUoZ2V0Q2VsbENvbERhdGEocHJvaikpICU+JSAKICBtdXRhdGUoU2FtcGxlID0gc3RyX3JlbW92ZShTYW1wbGUsICJzY0FUQUNfIikpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gVFNTRW5yaWNobWVudCwgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSAKCnAyIDwtIGFzX2RhdGFfZnJhbWUoZ2V0Q2VsbENvbERhdGEocHJvaikpICU+JSAKICBtdXRhdGUoU2FtcGxlID0gc3RyX3JlbW92ZShTYW1wbGUsICJzY0FUQUNfIikpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2dyaWRnZXM6Omdlb21fZGVuc2l0eV9yaWRnZXMoYWVzKHggPSBUU1NFbnJpY2htZW50LCB5ID0gU2FtcGxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gU2FtcGxlKSwgYWxwaGEgPSAwLjgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwMyA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fdmlvbGluKGFlcyh4ID0gU2FtcGxlLCB5ID0gVFNTRW5yaWNobWVudCwgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gU2FtcGxlLCB5ID0gVFNTRW5yaWNobWVudCxmaWxsID0gU2FtcGxlKSwgYWxwaGEgPSAwLjQpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiVFNTIEVucmljaG1lbnQiKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKYGBge3IsZmlnLndpZHRoPTEwfQpwMSA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpLCBsb2cxMF9uRnJhZ3MgPSBsb2cxMChuRnJhZ3MpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGxvZzEwX25GcmFncywgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KQoKcDIgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSwgbG9nMTBfbkZyYWdzID0gbG9nMTAobkZyYWdzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZ3JpZGdlczo6Z2VvbV9kZW5zaXR5X3JpZGdlcyhhZXMoeCA9IGxvZzEwX25GcmFncywgeSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAKCgpwMyA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpLCBsb2cxMF9uRnJhZ3MgPSBsb2cxMChuRnJhZ3MpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fdmlvbGluKGFlcyh4ID0gU2FtcGxlLCB5ID0gbG9nMTBfbkZyYWdzLCBmaWxsID0gU2FtcGxlKSwgYWxwaGEgPSAwLjgpICsKICBnZW9tX2JveHBsb3QoYWVzKHggPSBTYW1wbGUsIHkgPSBsb2cxMF9uRnJhZ3MsZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC40KSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAibnVtYmVyIG9mIGZyYWdtZW50cyIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKIyMjIFBsb3QgRnJhZ21lbnQgU2l6ZSBEaXN0cmlidXRpb24gJiBUU1MgRW5yaWNobWVudCBQcm9maWxlcwoKKiB0aGUgZGlzdHJpYnV0aW9uIG9mIGZyYWdtZW50cyBzaXplIGNhbiBiZSB2ZXJ5IGRpZmZlcmVudCBiZXR3ZWVuIHNhbXBsZXMsCmNlbGwgdHlwZXMgYW5kIGJhdGNoZXMgLT4gdGhlc2UgZGlmZmVyZW5jZXMgZG8gbm90IG5lY2Vzc2FyaWx5IGNvcnJlbGF0ZSB3aXRoIApkaWZmZXJlbmNlcyBpbiBxdWFsaXR5CiogdGhlIGRpcCBpcyB0aGUgZnJhZ21lbnQgc2l6ZSBvZiBhIG51Y2xlb3NvbWUgfjE0N2JwCiogVFNTIGVucmljaG1lbnQgcHJvZmlsZXMKICArIGNsZWFyIHBlYWsgaW4gdGhlIGNlbnRlciAKICArIHNtYWxsZXIgc2hvdWxkZXIgcGVhayByaWdodCBvZiB0aGUgY2VudGVyIGNhdXNlZCBieSB3ZWxsIHBvc2l0aW9uZWQgKzEgbnVjbGVvc29tZQoKYGBgI3tyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01fQpwMSA8LSBwbG90RnJhZ21lbnRTaXplcyhBcmNoUlByb2ogPSBwcm9qKQpwMiA8LSBwbG90VFNTRW5yaWNobWVudChBcmNoUlByb2ogPSBwcm9qKQpnZ0FsaWduUGxvdHMocDEsIHAyLCB0eXBlID0gInYiKQpgYGAKCiMgRmlsdGVyaW5nIERvdWJsZXRzCgpJbiB0aGUgcGxvdHMgYmVsb3cgdGhlIERvdWJsZXQgRW5yaWNobWVudCByZXByZXNlbnRzIHRoZSBlbnJpY2htZW50IG9mIHNpbXVsYXRlZApkb2J1bGV0cyBuZWFyYnkgZWFjaCBjZWxsLCBjb21wYXJlZCB0byB0aGUgZXhwZWN0ZWQgbnVtYmVyIG9mIGRvdWJsZXRzIGlmIGEgCnVuaWZvcm0gZGlzdHJpdXRpb24gaXMgYXNzdW1lZC4KClRoZSBkb3VibGV0IGRlbnNpdHkgdmlzc3VhbGl6ZXMgd2hlcmUgdGhlIHN5bnRoZXRpYyBkb3VibGV0cyBhcmUgbG9jYXRlZCBhZnRlciAKcHJvamVjdGluZyB0aGVtIGludG8gdGhlIDJEIGVtYmVkZGluZy4gCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQ9MTAsIHJlc3VsdHM9ICJhc2lzIn0KIyB0aGUgZG91YmxldCBpbmZvcm1hdGlvbiBpcyBzYXZlZCBpbiBhIHNpbXBsZUxpc3RvYmplY3QKCgoKZGlyIDwtICJRdWFsaXR5Q29udHJvbC8iCgoKZG91YmxldF9lbnJpY2htZW50IDwtIG1hcChzdGFnZXMsIGZ1bmN0aW9uKG4pIHsKICBwYXRoIDwtIHBhc3RlMChkaXIsIG4sICIvIikKICBhbGxfZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoKQogIGZpbGUgPC0gYWxsX2ZpbGVzW2dyZXBsKHBhc3RlMChuLCAiLURvdWJsZXQtU3VtbWFyeS5yZHMiKSwgYWxsX2ZpbGVzKV0KICAjIHJlYWQgaW4gZmlsZSBjb250YWluaW5nIGRvdWJsZXQgc3VtbWFyeSBmb3Igc2FtcGxlIG4gCiAgZG91YmxldF9zdW1tYXJ5IDwtIHJlYWRSRFMocGFzdGUwKGRpciwgbiwgIi8iLCBmaWxlKSkKICBkb3VibGV0X3N1bW1hcnlbWzFdXSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBYMSwgeSA9IFgyLCBjb2wgPSBlbnJpY2htZW50KSwgc2l6ZSA9IC44KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iRG91YmxldEVucmljaG1lbnQiKSkgKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoIlNpbXVsYXRlZCBEb3VibGV0IEVucmljaG1lbnQiLCBuKSkKfSkKICAKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBkb3VibGV0X2VucmljaG1lbnQsIG5jb2wgPSAyKQoKCgpkb3VibGV0X2RlbnNpdHkgPC0gbWFwKHN0YWdlcywgZnVuY3Rpb24obikgewogIHBhdGggPC0gcGFzdGUwKGRpciwgbiwgIi8iKQogIGFsbF9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGgpCiAgZmlsZSA8LSBhbGxfZmlsZXNbZ3JlcGwocGFzdGUwKG4sICItRG91YmxldC1TdW1tYXJ5LnJkcyIpLCBhbGxfZmlsZXMpXQogICMgcmVhZCBpbiBmaWxlIGNvbnRhaW5pbmcgZG91YmxldCBzdW1tYXJ5IGZvciBzYW1wbGUgbiAKICBkb3VibGV0X3N1bW1hcnkgPC0gcmVhZFJEUyhwYXN0ZTAoZGlyLCBuLCAiLyIsIGZpbGUpKQogIGRvdWJsZXRfc3VtbWFyeVtbMl1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgsIHkgPSB5LCBjb2wgPSBkZW5zaXR5KSwgc2l6ZSA9IC4xKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZG91YmxldF9zdW1tYXJ5W1sxXV0sIGFlcyh4ID0gWDEsIHkgPSBYMiksIHNpemUgPSAuMSwKICAgICAgICAgICAgIGFscGhhID0gLjMpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJTaW11bGF0ZWQgRG91YmxldCBEZW5zaXR5IikpICsKICBsYWJzKHRpdGxlID0gIlNpbXVsYXRlZCBkb3VibGV0IGRlbnNpdHkgb3ZlcmxheWVkIikKCn0pCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IGRvdWJsZXRfZGVuc2l0eSwgbmNvbCA9IDIpCmBgYAoKV2l0aCB0aGUgZnVuY3Rpb24gYGFkZERvdWJsZVNjb3JlcygpYCBpbmZvcm1hdGlvbiBvbiBwcmVkaWN0ZWQgZG91YmxldHMgaGFzIGJlZW4KYWRkZWQuIE5vdyB3ZSB3aWxsIGZpbHRlciB0aGUgcHV0YXRpdmUgZG91YmxldHMuIEFyY2hSIGF1dG9tYXRpY2FsbHkgcHJpbnRzIHRoZSBudW1iZXIgb2YKY2VsbHMgcmVtb3ZlZCBmcm9tIGVhY2ggc2FtcGxlIGFuZCB0aGUgY29ycmVzcG9uZGluZwpwZXJjZW50YWdlLiAKCioqYXJndW1lbnRzOioqCgoqIGN1dEVucmljaCA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0RW5yaWNobWVudCwgbnVtYmVyIG9mIHNpbXVsYXRlZCAKZG91YmxldHMgZGl2aWRlZCBieSBleHBlY3RlZCBudW1iZXIgZ2l2ZW4gYSByYW5kb20gdW5pZm9ybSBkaXN0cmlidXRpb24KKiBjdXRTY29yZSA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0IFNjb3JlLCByZXByZXNlbnRzIC1sb2cxMChiaW5vbWlhbCBhZGp1c3RlZCBwLXZhbHVlKQpmb3IgdGhlIERvdWJsZXRFbnJpY2htZW50YWRkCiogZmlsdGVyUmF0aW8gPSBtYXhpbXVtIHJhdGlvIG9mIHByZWRpY3RlZCBkb3VibGV0cyB0byBmaWx0ZXIgYmFzZWQgb24gbnVtYmVyIG9mIApwYXNzLWZpbHRlciBjZWxscyAoQSBoaWdoZXIgZmlsdGVyUmF0aW8gbWVhbnMgdGhhdCBtb3JlIGNlbGxzIGFyZSByZW1vdmVkKQplLmcuIDUwMDAgY2VsbHMKCm1heGltdW0gd291bGQgYmUgZmlsdGVyUmF0aW8gKiA1MDAwIC8gMTAwMDAwID0gZmlsdGVyUmF0aW8gKiA1MDAwICogMC4wNQoKKipUaGlzIHdheSBzYW1wbGVzIHdpdGggZGlmZmVyZW50IHBlcmNlbnRhZ2Ugb2YgZG91YmxldHMgd2lsbCBiZSBmaWx0ZXJlZCBhY2NvcmRpbmdseS4qKgoKQXMgY2FuIGJlIHNlZW4gYmVsb3csIGluIEU3LjVfcmVwMSBubyBkb3VibGV0cyBhcmUgZmlsdGVyZWQuIFdoeSBtaWdodCB0aGF0IGJlPwoKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CiMgaW4gb3VyIGNhc2Ugd2Ugbm93IGhhdmUgMTAgMjUxIGNlbGxzIGFzIG9wcG9zZWQgdG8gMTAgNjYxIGNlbGxzIGJlZm9yZQojIGZpbHRlcmluZyAtPiA0MTAgY2VsbHMgd2VyZSByZW1vdmVkICgzLjg1JSkKcHJvaiA8LSBmaWx0ZXJEb3VibGV0cyhBcmNoUlByb2ogPSBwcm9qKQpgYGAKCgoKIyBVc2UgYWxyZWFkeSBhbm5vdGF0ZWQgY2VsbHMKCiMjIFN1YnNldCBjZWxscwoKV2hhdCBpcyB0aGUgb3ZlcmxhcCBiZXR3ZWVuIG15IFFDIGFuZCB0aGUgcHJvY2Vzc2VkIGFuZCBhbm5vdGF0ZWQgZGF0YT8KCgpJbiB0b3RhbCBhZnRlciBRQyBhbmQgZG91YmxldCBmaWx0ZXJpbmcgdGhlIGRhdGFzZXQgY29udGFpbnMgNTksMTEzIGNlbGxzLiBGaXJzdCwgCndlIGhhdmUgdG8gcmVtb3ZlIHRoZSBjZWxscyBmcm9tIEU4Ljc1X3JlcDEvMiwgYmVjYXVzZSB0aGVzZSBzYW1wbGVzIGFyZQpub3QgaW5jbHVkZWQgaW4gdGhlIHByb2Nlc3NlZCBhbmQgYW5ub3RhdGVkIGRhdGEuIDgsODgwIGNlbGxzIGFyZSBmcm9tIHNhbXBsZXMgCkU4Ljc1X3JlcDEvMi4gCkluIHRoZSBwcm9jZXNzZWQgZGF0YSBtYXRyaXggd2UgZmluZCAzOCwxNTggY2VsbHMuIE9ubHkgMzAsODAzIGNlbGxzIHBhc3MgUk5BIApRQy4gT2YgdGhlIHNjUk5BLXNlcSBkYXRhc2V0IG9ubHkgMzAsMjgyIG91dCBvZiAzNiw4MjIgY2VsbHMgcGFzcyB0aGUgc2NBVEFDLXNlcQpRQy4gVGhpcyBtZWFucyB0aGF0IHdlIGhhdmUgMzAsMjgyIGNlbGxzIHdpdGggYW5ub3RhdGVkIGNlbGx0eXBlcy4gVGhlc2UgYXJlIAp0aGUgY2VsbHMgd2Ugd2FudCB0byB1c2UgaW4gdGhlIGZvbGxvd2luZyBhbmFseXNpcy4gV2Ugd2lsbCwgdGhlcmVmb3JlLCBzdWJzZXQKb3VyIGRhdGFzZXQuIFdlIGFyZSBub3cgbGVmdCB3aXRoIDI4LDE1NCBjZWxscyBmb3IgZnVydGhlciBhbmFseXNpcy4gQXBwYXJlbnRseSAKd2UgZmlsdGVyZWQgc29tZSBjZWxscyBub3QgZml0bGVyZWQgaW4gdGhlIHByb2Nlc3NlZCBkYXRhLgoKVG8gdmlzdWFsaXplIHRoaXMgaW4gYSBiZXR0ZXIgd2F5IGNyZWF0ZSBhIFZlbm4gZGlhZ3JhbS4KCmBgYHtyfQojIHJlYWQgaW4gcHJvY2Vzc2VkIHNjQVRBQy1zZXEgZGF0YQpwcm9jX2F0YWMgPC0gcmVhZEg1QUQoImFubmRhdGFfYXRhYy5oNWFkIikKCiMgcmVtb3ZlIGNlbGxzIHdoaWNoIGRvIG5vdCBwYXNzIHRoZSBybmFRQwpyZW1vdmUgPC0gKGFzLmRhdGEuZnJhbWUoY29sRGF0YShwcm9jX2F0YWMpKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihwYXNzX3JuYVFDID09IFRSVUUpKVtbImNlbGwiXV0KCmZpbHRfcHJvY2Vzc2VkX2F0YWMgPC0gcHJvY19hdGFjWyAsIGNvbG5hbWVzKHByb2NfYXRhYykgJWluJSByZW1vdmVdCgojIGNoZWNrIHRoZSBSTkEgZGF0YXNldCBhcyB3ZWxsIHRvIHNlZSBpZiB0aGUgY2VsbHMgb3ZlcmxhcCB3aXRoIHRoZSBBVEFDIGNlbGxzCnByb2Nfcm5hIDwtIHJlYWRINUFEKCJhbm5kYXRhX3JuYS5oNWFkIikKCgojIGtlZXAgdGhlIGNlbGxzIHdoaWNoIHBhc3MgYm90aCBybmFRQyBhbmQgYXRhY1FDCmtlZXAgPC0gY29sbmFtZXMocHJvY19ybmFbLGNvbG5hbWVzKHByb2Nfcm5hKSAlaW4lIGNvbG5hbWVzKHByb2NfYXRhYyldKQoKIyByZW1vdmUgdGhlIHNhbXBsZXMgRTguNzUKaWR4U2FtcGxlIDwtIEJpb2NHZW5lcmljczo6d2hpY2gocHJvaiRTYW1wbGUgJWluJSBjKCJFNy41X3JlcDEiLCAiRTcuNV9yZXAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFOC4wX3JlcDEiLCAiRTguMF9yZXAyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRTguNV9yZXAxIiwgIkU4LjVfcmVwMiIpKQpmaWx0X3Byb2ogPC0gcHJvaltwcm9qJGNlbGxOYW1lc1tpZHhTYW1wbGVdXQpgYGAKCkluIHRoZSBmb2xsb3dpbmcgcGxvdCB5b3UgY2FuIHNlZSB0aGUgY2VsbHMgd2hpY2ggcGFzc2VkIHF1YWxpdHkgY29udHJvbCBmb3IgCnNjUk5BLXNlcSwgc2NBVEFDLXNlcSBhbmQgd2hpY2ggYXJlIGluY2x1ZGVkIGluIHRoZSBBcmNoUiBvYmplY3Qgd2l0aCBhIHZlcnkgCmxvb3NlIHF1YWxpdHkgY29udHJvbC4gV2Ugd2lsbCBwcm9jZWVkIG9ubHkgdXNpbmcgdGhlIG92ZXJsYXAgYmV0d2VlbiB0aGUgdGhyZWUsCmJlY2F1c2UgZm9yIHRoZSBvdmVybGFwIHRoZSBjZWxsdHlwZXMgYXJlIGFubm90YXRlZCBhbHJlYWR5LiBUaGVzZSBhcmUgYWxzbyB0aGUgCmNlbGxzIHdoaWNoIGFyZSB1c2VkIGFzIGFuIGlucHV0IHRvIHNjRG9yaS4KCmBgYCN7cn0KIyBwbG90IHRoZSBvdmVybGFwIGJldHdlZW4gdGhlIGRpZmZlcmVudCB2ZXJzaW9ucwpwIDwtIGdnVmVubkRpYWdyYW06OmdnVmVubkRpYWdyYW0obGlzdChjb2xuYW1lcyhwcm9jX2F0YWMpLCBjb2xuYW1lcyhwcm9jX3JuYSksIGZpbHRfcHJvaiRjZWxsTmFtZXMpLAogICAgICAgICAgICAgIGxhYmVsX2FscGhhID0gMCwKICAgICAgICAgICAgICBjYXRlZ29yeS5uYW1lcz0gYygicHJvY2Vzc2VkIEFUQUMiLCAicHJvY2Vzc2VkIFJOQSIsICJBcmNoUiBwcm9qZWN0IiksCiAgICAgICAgICAgICAgI3NldC5zaXplID0gMjAsCiAgICAgICAgICAgICAgbGFiZWwgPSAiY291bnQiLAogICAgICAgICAgICAgIGxhYmVsX3NpemUgPSA2KSArCiAgIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGUgPSAiUmRCdSIpICsKICBsYWJzKHRpdGxlID0gIkFsbCBURnMgd2l0aCBkaWZmZXJlbnQgYWN0aXZpdHkiKQpwCmBgYAoKCmBgYHtyfQojIGtlZXAgb25seSB0aGUgY2VsbHMgd2hpY2ggcGFzc2VkIFJOQSBRQyBhbmQgQVRBQyBRQyAtPiBjZWxsdHlwZSBhbm5vdGF0aW9ucwpvdmVybGFwIDwtIGZpbHRfcHJvaiRjZWxsTmFtZXNbZmlsdF9wcm9qJGNlbGxOYW1lcyAlaW4lIGtlZXBdCgojIGZpbHRlciB0aGUgQXJjaFIgb2JqZWN0CmZpbHRfcHJvaiA8LSBmaWx0X3Byb2pbb3ZlcmxhcCwgXQoKIyBzdWJzZXQgYm90aCB0aGUgcHJvY2Vzc2VkIGF0YWMgZGF0YSBhbmQgcm5hIGRhdGEgdG8gZ2V0IG9ubHkgdGhlIG92ZXJsYXBwaW5nIGNlbGxzCnJuYV9vdmVybGFwIDwtIHByb2Nfcm5hWywgY29sbmFtZXMocHJvY19ybmEpICVpbiUgb3ZlcmxhcF0gCgphdGFjX292ZXJsYXAgPC0gcHJvY19hdGFjWywgY29sbmFtZXMocHJvY19hdGFjKSAlaW4lIG92ZXJsYXBdCgpgYGAKCgpTYXZlIHRoZSBzdWJzZXQgQXJjaFIgcHJvamVjdCBhbmQgcHJvY2VlZCB3aXRoIGl0IGZvciBub3cuIEl0IG1pZ2h0IGJlIGludGVyZXN0aW5nCnRvIHJldmlzaXQgYW5kIHNlZSBpZiBJIGNhbiBjbHVzdGVyIGFuZCBpZGVudGlmeSB0aGUgY2VsbCB0eXBlcyBkZSBub3ZvIGF0IGEgbGF0ZXIKc3RhZ2UhCgoKIyBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gCgoqIHR3byBvdGhlciBhbGdvcml0aG1zOgogICsgbGF0ZW50IHNlbWFudGljIGluZGV4aW5nIChMU0kpIGluIFNpZ25hYwogICsgbGFuZG1hcmsgZGlmZnVzaW9uIG1hcHMgKExETSkgaW4gU25hcEFUQUMKICAKKiBBcmNoUjogb3B0aW1pemVkIGl0ZXJhdGl2ZSBMU0kgbWV0aG9kIAogICsgZXhoaWJpdHMgbGVzcyBzdXNjZXB0aWJpbGl0eSB0byBiYXRjaCBlZmZlY3RzIAogICsgZm9jdXNlcyBvbiBtb3N0IHZhcmlhYmxlIGZlYXR1cmVzIAogIDEuIGNyZWF0ZSBhIExTSSBSZWR1Y3Rpb24gZnJvbSBhIHN1YnNldCBvZiB0aGUgdG90YWwgY2VsbHMKICAyLiBsaW5lYXJseSBwcm9qZWN0IHRoZSByZW1haW5pbmcgY2VsbHMgaW50byB0aGlzIHN1YnNwYWNlIHdpdGggTFNJIHByb2plY3Rpb24KICAoYmFzZWQgb24gU1ZEKQoKQmVjYXVzZSB3ZSBjYW4gaGF2ZSBtYXhpbWFsbHkgdHdvIGFjY2Vzc2libGUgYWxsZWxlcyBwZXIgY2VsbCwgdGhlIHNjQVRBQy1zZXEgZGF0YQppcyBzcGFyc2UuIFRoZXJlZm9yZSwgdGhlIG1ham9yaXR5IG9mIGFjY2Vzc2libGUgcmVnaW9ucyBhcmUgbm90IHRyYW5zcG9zZWQsIG1lYW5pbmcgCnRoYXQgbW9zdCBsb2NpIHdpbGwgaGF2ZSAwIGFjY2Vzc2libGUgYWxsZWxlcy4gQSB6ZXJvIGNvdWxkIG1lYW4gIm5vbi1hY2Nlc3NpYmxlIiAKb3IgIm5vdCBzYW1wbGVkIi4gRm9yIG1hbnkgYW5hbHlzaXMgd2UgY2FuIHVzZSBhIGJpbmFyaXplZCBtYXRpeC4gKipJbXBvcmFudGx5LCoqCioqdGhlIDFzIGhhdmUgaW5mb3JtYXRpb24sIEJVVCB0aGUgMHMgZG8gbm90ISoqCgpBIFBDQSB3b3VsZCByZXN1bHQgaW4gaGlnaCBpbnRlci1jZWxsIHNpbWlsYXJpdHkgYXQgYWxsIDAgcG9zaXRpb25zLiBBbiBhbHRlcm5hdGl2ZQphcHByb2FjaCBmb3IgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGlzIGEgKipsYXllcmVkIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbioqLiBGaXJzdCwKKipMYXRlbnQgU2VtYW50aWMgSW5kZXhpbmcgKExTSSkqKiBpcyB1c2VkLiBMU0kgaXMgYW4gYXBwcm9hY2ggZnJvbSBsYW5ndWFnZQpwcm9jZXNzaW5nLiBEaWZmZXJlbnQgc2FtcGxlcyBhcmUgdGhlICJkb2N1bWVudHMiIGFuZCBkaWZmZXJlbnQgcmVnaW9ucy9wZWFrcyBhcmUKdGhlICJ3b3JkcyIuIAoKIyMgSXRlcmF0aXZlIExTSQoKMS4gY29tcHV0ZSB0ZXJtIGZyZXF1ZW5jeSAoZGVwdGggbm9ybWFsaXphdGlvbiB0byAxMCwwMDAgcGVyIHNpbmdsZSBjZWxsKQoKJFRGID0gXGZyYWN7Q197aWp9fXtGX3tqfX0kLCB0aGUgdGVybSBmcmVxdWVuY3kgb2YgJENfe2lqfSQgd2hpY2ggaXMgdGlsZXMgaSAKaW4gY2VsbCBqIGFuZCAkRl97an0kIGJlaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgdGlsZXMgd2l0aCBpbnNlcnRpb25zIGluIGNlbGwgai4KCjIuIEludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5IAogICogd2VpZ2h0cyBmZWF0dXJlcyBieSBob3cgb2Z0ZW4gdGhleSBvY2N1ciAKICAqIG1vcmUgd2VpZ2h0IHRvIGxlc3MgZnJlcXVlbnQgcGVha3MKCiRJREZfe2l9ID1cbG9nIHtcZnJhY3tOfXsxICsgREZfe2l9fSQgd2l0aCBOIGJlaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgY2VsbHMgaW4gdGhlIGRhdGFzZXQgCmFuZCAkREZfe2l9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNlbGxzIChkb2N1bWVudHMpIGluIHdoaWNoIHRoZSB0aWxlIGkgaXMgcHJlc2VudC4KCjMuIFRoZSB0ZXJtIGZyZXF1ZW5jeSBURiBpcyBub3JtYWxpemVkIGJ5IHRoZSBpbnZlcnNlIGRvY3VtZW50IGZyZXF1bmN5IElERi4gCllvdSBnZXQgYSAqKlRGLUlERioqIG1hdHJpeCAodGVybSBmcmVxdWVuY3ktaW52ZXJzZSBkb2N1bWVudCBmcmVxdWVuY3kpIHdoaWNoCnRlbGxzIHVzIGhvdyBpbXBvcnRhbnQgYSByZWdpb24vcGVhayBpcyB0byBhIHNhbXBsZS4gSW4gb3RoZXIgd29yZHMgeW91IHRyYW5zZm9ybQphIGJpbmFyeSBtYXRyaXggdG8gYSBub24tYmluYXJ5IG1hdHJpeC4KCiRURi1JREYgPSBcbG9nezEgKyBURntpan19ICogSURGX3tpfSAqIDEwXns0fX0kCgo0LiBTVkQgaWRlbnRpZmllcyB0aGUgbW9zdCB2YWx1YWJsZSBpbmZvcm1hdGlvbiBhY3Jvc3Mgc2FtcGxlcy4gVGhlbiAKd2UgY2FuIHVzZSB0aGVzZSBtb3N0IHZhbHVhYmxlIGZlYXR1cmVzIHRvIHJlcHJlc2VudCB0aGUgZGF0YSBpbiBhIGxvd2VyIGRpbWVuc2lvbmFsIHNwYWNlCjUuIENsdXN0ZXJzIGFyZSBpZGVudGlmaWVkIHdpdGggU2V1cmF0J3MgU2hhcmVkIE5lYXJlc3QgTmVpZ2hib3IgY2x1c3RlcmluZwo2LiBTdW0gYWNjZXNzaWJpbGl0eSBhY3Jvc3MgYWxsIHNpbmdsZSBjZWxscyBpbiBlYWNoIGNsdXN0ZXIgLT4gbG9nLW5vcm1hbGl6ZQo3LiBJZGVudGlmeSBtb3N0IHZhcmlhYmxlIGZlYXR1cmVzIGFjcm9zcyB0aGUgY2x1c3RlcnMKOC4gcmVwZWF0IHdpdGggbW9zdCB2YXJpYWJsZSBwZWFrcyBhcyBmZWF0dXJlcwoKV2l0aCBMU0kgd2UgY2FuIHJlZHVjZSB0aGUgZGltZW5zaW9uYWxpdHkgb2YgdGhlIHNwYXJzZSBpbnNlcnRpb24gbWF0cml4IHRvIHRlbnMgCm9yIGh1bmRyZWRzLiBUaGVuIFVNQVAgb3IgdC1TTkUgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXplIHRoZSBkYXRhCgoKVW5saWtlIGluIHNjUk5BLXNlcSB3ZSBjYW5ub3Qgc2VsZWN0IHRoZSB0b3AgaGlnaGx5IHZhcmlhYmxlIGZlYXR1cmVzIGJlZm9yZSAKZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIChoaWdoIG5vaXNlLCBsb3cgcmVwcm9kdWNpYmlsaXR5KS4gUmF0aGVyIHRoZSBpdGVyYXRpdmUgCkxTSSBhcHByb2FjaCBmaXJzdCBjb21wdXRlcyBhIExTSSAKb24gdGhlIG1vc3QgYWNjZXNzaWJsZSB0aWxlcyAodGhpcyB3aWxsIGlkZW50aWZ5IGNsdXN0ZXJzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIAptYWpvciBjZWxsIHR5cGVzKS4gVGhlbiwgQXJjaFIgY29tcHV0ZXMgdGhlIGF2ZXJhZ2UgYWNjZXNzaWJpbGl0eSBhY3Jvc3MgdGhlc2UgCmNsdXN0ZXJzIGFjcm9zcyBhbGwgZmVhdHVyZXMuIE5leHQsIHRoZSBtb3N0IHZhcmlhYmxlIHBlYWtzIGFjcm9zcyB0aGVzZSBjbHVzdGVycwphcmUgaWRlbnRpZmllZC4gVGhlIG1vc3QgaGlnaGx5IGFjY2Vzc2libGUgcGVha3MgYXJlIHRoZSBmZWF0dXJlcyBvZiBhIG5ldyAKcm91bmQgb2YgTFNJLiBXZSBjYW4gc2V0IGhvdyBtYW55IHJvdW5kcyBvZiBMU0kgd2Ugd2FudCB0byBiZSBwZWZvcm1lZC4gCgpVc2luZyBpdGVyYXRpdmUgTFNJIHJlZHVjZXMgYmF0Y2ggZWZmZWN0cy4gSWYgeW91IHNlZSBzb21lIGJhdGNoIGVmZmVjdHMgeW91IGNvdWxkCnRyeSB0byBhZGQgbW9yZSBMU0kgaXRlcmF0aW9ucyBhbmQgc3RhcnQgZnJvbSBhIGxvd2VyIGluaXRpYWwgY2x1c3RlcmluZyAKcmVzb2x1dGlvbi4gQWxzbywgdGhlIG51bWJlciBvZiB2YXJpYWJsZSBmZWF0dXJlcyBjYW4gYmUgbG93ZXJlZC4gICMKCgpgYGAje3J9CiNmaWx0X3Byb2ogPC0gYWRkSXRlcmF0aXZlTFNJKEFyY2hSUHJvaiA9IGZpbHRfcHJvaiwgdXNlTWF0cml4ID0gIlRpbGVNYXRyaXgiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgbmFtZSA9ICJhdGFjX0xTSV8yNTAwMCIpCiNmaWx0X3Byb2ogPC0gYWRkSXRlcmF0aXZlTFNJKEFyY2hSUHJvaiA9IGZpbHRfcHJvaiwgdXNlTWF0cml4ID0gIlRpbGVNYXRyaXgiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgbmFtZSA9ICJhdGFjX0xTSV81MDAwMCIpCmZpbHRfcHJvaiA8LSBhZGRJdGVyYXRpdmVMU0koQXJjaFJQcm9qID0gZmlsdF9wcm9qLCB1c2VNYXRyaXggPSAiVGlsZU1hdHJpeCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiYXRhY19MU0lfMTAwMDAwIikKCmBgYAoKCiMjIENsdXN0ZXJpbmcKCkNhbGxpbmcgY2x1c3RlcnMgaW4gdGhpcyBuZXcgc3BhY2UgdXNlcyB0aGUgU2V1cmF0J3MgZ3JhcGggY2x1c3RlcmluZyBmdW5jdGlvbgphcyBkZWZhdWx0IGNsdXN0ZXJpbmcgbWV0aG9kLiBUaGUgU2V1cmF0IAptZXRob2QgZmlyc3QgY29tcHV0cyBLTk4gZ3JhcGggYW5kIHRoZW4gYSAgbW9kdWxhcml0eSBvcHRpbWl6YXRpb24KdGVjaG5pcXVlIHRvIGNsdXN0ZXIgdGhlIGNlbGxzIChpdGVyYXRpdmVseSBncm91cCBjZWxscyB0b2dldGhlcgp3aXRoIExvdXZpYW4gYWxnb3JpdGhtIHVzaW5nIDEwIHJhbmRvbSBzdGFydHMpLiBBbm90aGVyIG9wdGlvbiAKaXMgdG8gdXNlICJTY3JhbiIuIFRoZQpkZWZhdWx0IG51bWJlciBvZiBuZWFyZXN0IG5laWdoYm9ycyB1c2VkCmlzIDEwLiBUaGUgbWluaW11bSBudW1iZXIgb2YgY2VsbHMgZm9yIGEgY2x1c3RlciB0byBiZSBjYWxsZWQgYSAKY2x1c3RlciBpcyBzZXQgdG8gNSBieSBkZWZhdWx0LiBUaGUgbWF4aW11bSBudW1iZXIgb2YgY2x1c3RlcnMgdG8gCmJlIGNhbGxlZCBpcyBzZXQgdG8gMjUgYnkgZGVmYXVsdC4gCgpgYGAje3J9CnByb2ogPC0gYWRkQ2x1c3RlcnMoaW5wdXQgPSBwcm9qLCByZWR1Y2VkRGltcyA9ICJJdGVyYXRpdmVMU0kiKQpgYGAKCgojIFZpc3VhbGl6YXRpb24KCldlIHdpbGwgYWRkIHRoZSBjZWxsdHlwZSBhbm5vdGF0aW9uIGZyb20gdGhlIHByb2Nlc3NlZCBkYXRhIHRvIG91ciBBcmNoUiAKcHJvamVjdC4gU2luY2Ugd2UgYWxyZWFkeSBoYXZlIGFubm90YXRpb25zLCB3ZSBkbyBub3QgbmVlZCB0byBwZXJmb3JtIGNsdXN0ZXJpbmcuCgpgYGAje3J9CiNmaWx0X3Byb2ogPC0gYWRkVFNORShBcmNoUlByb2ogPWZpbHRfcHJvaiwgcmVkdWNlZERpbXMgPSAiYXRhY19MU0lfMTAwMDAwIikKCmZpbHRfcHJvaiA8LSBhZGRVTUFQKEFyY2hSUHJvaiA9ZmlsdF9wcm9qLCByZWR1Y2VkRGltcyA9ICJhdGFjX0xTSV8xMDAwMDAiKQpgYGAKCgoKYGBge3J9CmZpbHRfcHJvaiA8LSBsb2FkQXJjaFJQcm9qZWN0KHBhdGggPSAiMDJfZmlsdGVyZWRfYW5ub3RhdGVkX0FyY2hSb2JqZWN0LyIpCgoKIyBhZGQgY2VsbHR5cGUgYW5ub3RhdGlvbnMgZm9ybSB0aGUgcHJvY2Vzc2VkIGF0YWMgdG8gdGhlIEFyY2hSIG9iamVjdAoKIyBnZXQgdmVjdG9yIG9mIGNlbGx0eXBlIGFubm90YXRpb25zCmNlbGx0eXBlcyA8LSBhcy5jaGFyYWN0ZXIoYXRhY19vdmVybGFwJGNlbGx0eXBlLm1hcHBlZCkKCiMgbmFtZSBjZWxsdHlwZSB2ZWN0b3Igd2l0aCBjZWxsIG5hbWVzCm5hbWVzKGNlbGx0eXBlcykgPC0gY29sbmFtZXMoYXRhY19vdmVybGFwKQoKI2ZpbHRfcHJvaiA8LSBhZGRDZWxsQ29sRGF0YShmaWx0X3Byb2osCiAjICAgICAgICAgICAgICBkYXRhID0gY2VsbHR5cGVzLCAKICAjICAgICAgICAgICAgIG5hbWUgPSAiY2VsbHR5cGVzIiwKICAgIyAgICAgICAgICAgIGNlbGxzID0gY29sbmFtZXMoYXRhY19vdmVybGFwKQogICAgIyAgICAgICAgICAgKQpgYGAKCmBgYHtyfQojIGFkZCBjdXN0b20gY29sb3JzCmNvbFBhbGV0dGVfY2VsbHR5cGVzID0gYygnIzUzMkM4QScsCiAnI2MxOWY3MCcsCiAnI2Y5ZGVjZicsCiAnI2M5YTk5NycsCiAnI0I1MUQ4RCcsCiAnIzNGODRBQScsCiAnIzllNjc2MicsCiAnIzM1NEUyMycsCiAnI0YzOTdDMCcsCiAnI2ZmODkxYycsCiAnIzYzNTU0NycsCiAnI0M3MjIyOCcsCiAnI2Y3OTA4MycsCiAnI0VGNEUyMicsCiAnIzk4OTg5OCcsCiAnIzdGNjg3NCcsCiAnIzg4NzBhZCcsCiAnIzY0N2E0ZicsCiAnI0VGNUE5RCcsCiAnI0ZCQkU5MicsCiAnIzEzOTk5MicsCiAnI2NjNzgxOCcsCiAnI0RGQ0RFNCcsCiAnIzhFQzc5MicsCiAnI0M1OTRCRicsCiAnI0MzQzM4OCcsCiAnIzBGNEE5QycsCiAnI0ZBQ0IxMicsCiAnIzhEQjVDRScsCiAnIzFBMUExQScsCiAnI0M5RUJGQicsCiAnI0RBQkU5OScsCiAnIzY1QTgzRScsCiAnIzAwNTU3OScsCiAnI0NERTA4OCcsCiAnI2Y3Zjc5ZScsCiAnI0Y2QkZDQicpCgpjZWxsdHlwZXMgPC0gKGFzLmRhdGEuZnJhbWUoZ2V0Q2VsbENvbERhdGEoZmlsdF9wcm9qKSkgJT4lIGdyb3VwX2J5KGNlbGx0eXBlcykgJT4lIAogc3VtbWFyaXNlKG4gPSBuKCkpKSRjZWxsdHlwZXMKCmNvbCA8LSBzZXROYW1lcyhjb2xQYWxldHRlX2NlbGx0eXBlcywgY2VsbHR5cGVzKQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTEwfQpkZiA8LSBhc19kYXRhX2ZyYW1lKGNiaW5kKGdldENlbGxDb2xEYXRhKGZpbHRfcHJvaiksIGdldEVtYmVkZGluZyhmaWx0X3Byb2opKSApICU+JQogIHJlbmFtZShjKHVtYXAxID0gYXRhY19MU0lfMTAwMDAwLlVNQVBfRGltZW5zaW9uXzEsIHVtYXAyICA9IGF0YWNfTFNJXzEwMDAwMC5VTUFQX0RpbWVuc2lvbl8yKSkKCnZhcmlhYmxlcyA8LSBjKCJjZWxsdHlwZXMiLCAiU2FtcGxlIiwgIm5GcmFncyIsICJEb3VibGV0U2NvcmUiKQoKCnBsb3QxIDwtIG1hcChjKCJjZWxsdHlwZXMiKSwgZnVuY3Rpb24obil7CiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkZiAlPiUgcHVsbCgidW1hcDEiKSwgeSA9IGRmICU+JSBwdWxsKCJ1bWFwMiIpLCAKICAgICAgICAgICAgICAgICBjb2wgPSBkZiAlPiUgcHVsbChuKSksICBzaXplID0gLjgpICsKICAgIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPXBhc3RlMChuKSkpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2wpKwogICAgeGxhYigidW1hcDEiKSArCiAgICB5bGFiKCJ1bXBhMiIpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZTAobikpCn0pCgpwbG90czIgPC0gbWFwKGMoIm5GcmFncyIsICJEb3VibGV0U2NvcmUiKSwgZnVuY3Rpb24obil7CiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkZiAlPiUgcHVsbCgidW1hcDEiKSwgeSA9IGRmICU+JSBwdWxsKCJ1bWFwMiIpLCAKICAgICAgICAgICAgICAgICBjb2wgPSBkZiAlPiUgcHVsbChuKSksIHNpemUgPSAuOCkgKwogICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogICAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPXBhc3RlMChuKSkpICsKICAgIHhsYWIoInVtYXAxIikgKwogICAgeWxhYigidW1wYTIiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKG4pKQp9KQoKcGxvdDMgPC0gbWFwKGMoIlNhbXBsZSIpLCBmdW5jdGlvbihuKXsKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRmICU+JSBwdWxsKCJ1bWFwMSIpLCB5ID0gZGYgJT4lIHB1bGwoInVtYXAyIiksIAogICAgICAgICAgICAgICAgIGNvbCA9IGRmICU+JSBwdWxsKG4pKSwgIHNpemUgPSAuOCkgKwogICAgZ3VpZGVzKGNvbD1ndWlkZV9sZWdlbmQodGl0bGU9cGFzdGUwKG4pKSkgKwogICAgeGxhYigidW1hcDEiKSArCiAgICB5bGFiKCJ1bXBhMiIpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZTAobikpCn0pCgoKcGxvdDEKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xMH0KZG8uY2FsbChncmlkRXh0cmE6OmdyaWQuYXJyYW5nZSwgYyhwbG90MywgcGxvdHMyLCBuY29sPTMpKSMsIG5yb3cgPSAyKSkKCmBgYAoKCgpgYGB7cn0KI3NhdmVBcmNoUlByb2plY3QoZmlsdF9wcm9qLCBvdXRwdXREaXJlY3RvcnkgPSAiMDJfZmlsdGVyZWRfYW5ub3RhdGVkX0FyY2hSb2JqZWN0IiwgbG9hZCA9IEZBTFNFKQoKCmBgYAoKCgoKCiMgQ2hlY2sgZ2VuZSBzY29yZXMgZm9yIHRoZSBkaWZmZXJlbnQgY2VsbHR5cGVzCgoKCmBgYHtyfQpmaWx0X3Byb2ogPC0gYWRkSW1wdXRlV2VpZ2h0cyhmaWx0X3Byb2osIHJlZHVjZWREaW1zID0gImF0YWNfTFNJXzEwMDAwMCIpCmBgYAoKYGBge3J9Cm1hcmtlckdlbmVzICA8LSBjKAogICAgIkRubXQzYiIsICAjIEVwaWdibGFzCiAgICAiUHRuIiwgIyBGb3JlYnJhaW4vTWlkYnJhaW4vSGluZGJyYWluCiAgICAiUG91NWYxIiwgIyBQcmltaXRpdmUgU3RyZWFrCiAgICAiTXlsNyIsICMgY2FyZGlvbXlvY3l0ZXMKICAgICJIb3hhMTAiICNBbGxhbnRvaXMKCiAgKQpgYGAKCmBgYCN7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpwIDwtIHBsb3RFbWJlZGRpbmcoCiAgICBBcmNoUlByb2ogPSBmaWx0X3Byb2osIAogICAgY29sb3JCeSA9ICJHZW5lU2NvcmVNYXRyaXgiLCAKICAgIG5hbWUgPSBtYXJrZXJHZW5lcywgCiAgICBlbWJlZGRpbmcgPSAiVU1BUCIsCiAgICBpbXB1dGVXZWlnaHRzID0gZ2V0SW1wdXRlV2VpZ2h0cyhwcm9qKQopCmRvLmNhbGwoZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UsIGMocCwgbmNvbCA9IDMpKQpgYGAKCgojIFZpc3VhbGl6aW5nIEdlbm9tZSBCcm93c2VyIFRyYWNrcwoKQnJvd3NlIGxvY2FsIGNocm9tYXRpbiBhY2Nlc3NpYmlsaXR5IGF0IG1hcmtlciBnZW5lcy4gUGxvdCBnZW5vbWUKYnJvd3NlciB0cmFja3MgcGVyIGNlbGx0eXBlIAoKYGBgI3tyfQpwIDwtIHBsb3RCcm93c2VyVHJhY2soCiAgICBBcmNoUlByb2ogPSBmaWx0X3Byb2osIAogICAgZ3JvdXBCeSA9ICJjZWxsdHlwZXMiLCAKICAgIGdlbmVTeW1ib2wgPSBtYXJrZXJHZW5lcywgIyB0aGUgcGxvdCB3aW5kb3cgaXMgY2VudGVyZWQgYXQgdGhlIFRTUwogICAgdXBzdHJlYW0gPSA1MDAwMCwKICAgCiAgICAgZG93bnN0cmVhbSA9IDUwMDAwCikKCnN0YWdlc19wbG90cyA8LSBwbG90QnJvd3NlclRyYWNrKAogICAgQXJjaFJQcm9qID0gZmlsdF9wcm9qLCAKICAgIGdyb3VwQnkgPSAiU2FtcGxlIiwgCiAgICBnZW5lU3ltYm9sID0gbWFya2VyR2VuZXMsIAogICAgdXBzdHJlYW0gPSA1MDAwMCwKICAgCiAgICAgZG93bnN0cmVhbSA9IDUwMDAwCikKYGBgCgoKYGBgI3tyfQptYXJrZXJHZW5lcyAgPC0gYygKICAgICJEbm10M2IiLCAgIyBFcGlnYmxhcwogICAgIlB0biIsICMgRm9yZWJyYWluL01pZGJyYWluL0hpbmRicmFpbgogICAgIlBvdTVmMSIsICMgUHJpbWl0aXZlIFN0cmVhawogICAgIk15bDciLCAjIGNhcmRpb215b2N5dGVzCiAgICAiSG94YTEwIiAjQWxsYW50b2lzCgogICkKYGBgCgoKCiAgICAKKiBEbm10M2IgPSBFcGlibGFzdCAKKiBQdG4gPSBGb3JlYnJhaW4vTWlkYnJhaW4vSGluZGJyYWluCiogUG91ZjVmMSA9IFByaW1pdGl2ZSBTdHJlYWsKKiBNeWw3ID0gY2FyZGlvbXlvY3l0ZXMKKiBIb3hhMTAgPSBBbGxhbnRvaXMKClNpbmNlIHdlIGdyb3VwIHRoZSBjZWxscyBhY2NvcmluZyB0byBjZWxsdHlwZSBiZWxvdyBhbmQgcGxvdCB0aGUgYXZlcmFnZQpzaWduYWwvYWNjZXNzaWJpbGl0eS4gU2luY2Ugc29tZSBzaWduYWxzIGFyZSBvbmx5IGV4cGVjdGVkIGluIGEgc3Vic2V0IG9mIHRoZQplbWJyeW9uaWMgc3RhZ2VzLCB0aGUgc2lnbmFscyBtaWdodCBiZSBkaWx1dGVkIGlmIHdlIGxvb2sgYXQgYWxsIHN0YWdlcyBhdCBvbmNlCmFuZCB0YWtlIHRoZSBtZWFuIGFjY2Vzc2liaWxpdHkgb3ZlciBkaWZmZXJlbnQgc3RhZ2VzLiBJdCB3b3VsZCBwcm9iYWJsCgpgYGAje3IsZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTEwfQpmb3IgKGVsZW0gaW4gcCl7CiAgZ3JpZDo6Z3JpZC5uZXdwYWdlKCkKICBncmlkOjpncmlkLmRyYXcoZWxlbSkKfQoKYGBgCgpgYGAje3J9CmZvciAoZWxlbSBpbiBzdGFnZXNfcGxvdHMpewogIGdyaWQ6OmdyaWQubmV3cGFnZSgpCiAgZ3JpZDo6Z3JpZC5kcmF3KGVsZW0pCn0KYGBgCgoKIyBEaWZmZXJlbnQgVGltZSBwb2ludHMKClRvIGRpZmZlcmVudGlhdGUgYmV0d2VlbiBleHByZXNzaW9uIGF0IGRpZmZlcmVudCBzdGFnZXMgd2Ugd2lsbCBoYXZlIHRvIHNwbGl0CnRoZSBBcmNoUiBwcm9qZWN0cyBpbnRvIAoKCgojIENvbWJpbmUgd2l0aCBSTkEtc2VxIGRhdGEKClNpbmNlIHdlIGFyZSB1c2luZyBtdWx0aW9tZSBkYXRhLCB3ZSBkbyBub3QgbmVlZCB0byBpbnRlZ3JhdGUgdGhlIEFUQUMgYW5kIFJOQQpkYXRhLCBidXQgd2UgaGF2ZSBib3RoIGluZm9ybWF0aW9ucyBmcm9tIHRoZSBzYW1lIGNlbGwuIFdlIHdpbGwgdXNlIHRoZSBwcm9jZXNzZWQKc2NSTkEtc2VxIGRhdGEgd2hpY2ggd2UgZmlsdGVyZWQgdG8gY29udGFpbiBvbmx5IHRoZSBjZWxscyB3aGljaCBvdmVybGFwIHdpdGggCnRoZSBjZWxscyBpbiB0aGUgQXJjaFIgb2JqZWN0LgoKTG9vayBoZXJlIGZvciBtb3JlIGRldGFpbHMgb24gbXVsdGlvbWUgYW5hbHlzaXMgaW4gQXJjaFI6Cmh0dHBzOi8vZ3JlZW5sZWFmbGFiLmdpdGh1Yi5pby9BcmNoUl8yMDIwL0V4LUFuYWx5emUtTXVsdGlvbWUuaHRtbAoKT25lIGludGVyZXN0aW5nIHRoaW5nIHRvIG5vdGljZSBpcyB0aGF0IHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGdlbmUgc2NvcmUgCm1hdHJpeCBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgbnVtYmVyIG9mIGdlbmVzIGluIHRoZSBSTkEgZGF0YXNldC4KCkkgZ2V0IGFuIGVycm9yIHdoZW4gdHJ5aW5nIHRvIGV4dHJhY3QgdGhlIHRpbGUgbWF0cml4LiBOb3Qgc3VyZSB3aGF0IGlzIGhhcHBlbmluZwp0aGVyZS4gCgpgYGB7cn0KIyBleHRyYWN0IGdlbmUgc2NvcmUgbWF0cml4CmdlbmVfc2NvcmVzIDwtIGdldE1hdHJpeEZyb21Qcm9qZWN0KGZpbHRfcHJvaiwgdXNlTWF0cml4ID0gIkdlbmVTY29yZU1hdHJpeCIpCmdlbmVfc2NvcmVzX210eCA8LSBhc3NheXMoZ2VuZV9zY29yZXMpW1sxXV0KCiMgc2V0IG5hbWVzIG9mIHRoZSBnZW5lIHNjb3JlIG1hdHJpeApyb3duYW1lcyhnZW5lX3Njb3Jlc19tdHgpIDwtIHJvd0RhdGEoZ2VuZV9zY29yZXMpJG5hbWUKCiN0aWxlX21hdHJpeCA8LSBnZXRNYXRyaXhGcm9tUHJvamVjdChmaWx0X3Byb2osIHVzZU1hdHJpeCA9ICJUaWxlTWF0cml4IiwgYmluYXJpemUgPSBUUlVFKQoKYGBgCgoKCmBgYHtyfQpwcmludChwYXN0ZTAoIlRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIEFUQUMtc2VxIEdlbmUgU2NvcmUgTWF0cml4IGlzOiAiLAogICAgICAgICAgICAgbnJvdyhnZW5lX3Njb3Jlc19tdHgpKSkKCnByaW50KHBhc3RlMCgiVGhlIG51bWJlciBvZiBnZW5lcyBpbiB0aGUgUk5BLXNlcSBHZW5lIEV4cHJlc3Npb24gTWF0cml4IGlzOiAiLAogICAgICAgICAgICAgZGltKHJuYV9vdmVybGFwKVsxXSkpCgpwcmludChwYXN0ZTAoIlRoZSBudW1iZXIgb2YgZ2VuZXMgZm91bmQgaW4gYm90aCB0aGUgR2VuZSBFeHByZXNzaW9uIE1hdHJpeCBhbmQgdGhlIEdlbmUgU2NvcmUgTWF0cml4OiIsIAogICAgICBsZW5ndGgoaW50ZXJzZWN0KHJvd25hbWVzKGdlbmVfc2NvcmVzX210eCksIHJvd25hbWVzKHJuYV9vdmVybGFwKSkpKSkKYGBgCgoKRmlsdGVyIGdlbmVzIHdoaWNoIGFyZSBmb3VuZCBpbiBnZW5lIHNjb3JlIG1hdHJpeCBhbmQgZ2VuZSBleHByZXNzaW9uIG1hdHJpeC4gUmVvcmRlcgpjZWxsIG5hbWVzIHRvIG1hdGNoIGJldHdlZW4gZ2VuZSBzY29yZXMgYW5kIGdlbmUgZXhwcmVzc2lvbi4gSGVyZSBpdCB3b3VsZApiZSBuaWNlIHRvIHNob3cgVmVubkRpYWdyYW1zIHRvIHF1aWNrbHkgYmUgYWJsZSB0byB1bmRlcnN0YW5kIHdoYXQgdGhlIGRpZmZlcmVuY2VzCmluIGdlbmVzIGFyZSBiZXR3ZWVuIEFyY2hSIG9iamVjdCBhbmQgZ2VuZSBleHByZXNzaW9uIG1hdHJpeC4gSXMgdGhlcmUgYSB3YXkKdG8gaW5jbHVkZSBhbHNvIGdlbmVzIHdoaWNoIGRvIG5vdCBvdmVybGFwPwoKYGBge3J9CiMgVHJ5IHRvIHJlb3JkZXIgdGhlIGNlbGxuYW1lcyB0byB0aGUgCgojIHRoaXMgaXMgdGhlIG9yZGVyIG9mIHRoZSBjZWxsIG5hbWVzIGluIHRoZSBBcmNoUiBwcm9qZWN0IGdlbmUgc2NvcmUgbWF0cml4Cm9yZGVyIDwtIHJvd25hbWVzKGdldENlbGxDb2xEYXRhKGZpbHRfcHJvaikpCgojIGV4dHJhY3QgdGhlIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggCnJuYV9jb3VudHMgPC0gYXNzYXlzKHJuYV9vdmVybGFwKVtbMV1dCgojIHJlb3JkZXIgZ2VuZSBleHByZXNzaW9uIG1hdHJpeCBjZWxsIG5hbWVzIHRvIG1hdGNoIHRoZSBjZWxsbmFtZXMgb2YgdGhlIEFyY2hSIG9iamVjdApybmFfY291bnRzIDwtIHJuYV9jb3VudHNbLCBvcmRlcl0KCiMgSSBjaGVja2VkIHRoYXQgdGhleSBhcmUgdGhlIHNhbWUKI2NvbG5hbWVzKHJuYV9jb3VudHMpID09IHJvd25hbWVzKGdldENlbGxDb2xEYXRhKGZpbHRfcHJvaikpCgojIGdldCB0aGUgZ2VuZSBuYW1lcyBvZiB0aGUgZ2VuZSBzY29yZSBtYXRyaXgKZ2VuZXMgPC0gcm93bmFtZXMoZ2VuZV9zY29yZXNfbXR4KQoKIyBzdWJzZXQgdGhlIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggdG8gb2J0YWluIG9ubHkgdGhlIGdlbmVzIGZvdW5kIGluIHRoZSBnZW5lIAojIHNjb3JlIG1hdHJpeApybmFfY291bnRzIDwtIHJuYV9jb3VudHNbcm93bmFtZXMocm5hX2NvdW50cykgJWluJSBnZW5lcyxdCmBgYAoKTmV4dCB3ZSBoYXZlIHRvIGNyZWF0ZSBhIEdSYW5nZXMgb2JqZWN0IHdoaWNoIHdpbGwgYmUgdXNlZCBpbiB0aGUgbmV4dCBzdGVwIHRvIApjcmVhdGUgYSBSYW5nZWRTdW1tYXJpemVkRXhwZXJpbWVudC4gV2hlbiB1c2luZyB0aGUgZ2VuZSBhbm5vdGF0aW9uIG1ldGFkYXRhCnN0b3JlZCBpbiB0aGUgcm93RGF0YSBvZiB0aGUgUk5BLVNDRSBmb3IgY3JlYXRpbmcgdGhpcyBSYW5nZWRTdW1tYXJpemVkRXhwZXJpbWVudAp3aXRoIHRoZSBBcmNoUm9iamVjdCwgSSBnb3QgYW4gZXJyb3IgbWVzc2FnZS4gVGhpcyBtaWdodCBoYXZlIGJlZW4gZHVlIHRvIHRoZQpmYWN0IHRoYXQgdGhlIGdlbmUgYW5ub3RhdGlvbnMgbG9va2VkIGRpZmZlcmVudCBiZXR3ZWVuIHRoZSBSTkEtU0NFIGFuZCB0aGUgQXJjaFIKb2JqZWN0LiBUaGVyZWZvcmUsIEkgdHJpZWQgdXNpbmcgdGhlIHNhbWUgZ2VuZSBhbm5vdGF0aW9uIGFzIEFyY2hSIHVzZXMuIEluIG9yZGVyIApmb3IgdGhpcyB0byB3b3JrLCB3ZSB3aWxsIGhhdmUgdG8gZXhjbHVkZSBhbnkgZ2VuZXMgd2hpY2ggYXJlIG5vdCB1c2VkIGJ5IEFyY2hSLgoKYGBge3J9CiMgZ2V0IHRoZSBHcmFuZ2VzIG9iamVjdCBmcm9tIHRoZSBBcmNociBwcm9qZWN0CmFyY2hyX2dyYW5nZXMgPC1nZXRHZW5lcyhmaWx0X3Byb2opCgojIGZpbHRlciBmb3IgZ2VuZXMgd2hpY2ggYXJlIGluY2x1ZGVkIGluIHRoZSBnZW5lIGV4cHJlc3Npb24gbWF0cml4CmdyYW5nZXMgPC0gc3Vic2V0KGFyY2hyX2dyYW5nZXMsIHN5bWJvbCAlaW4lIHJvd25hbWVzKHJuYV9jb3VudHMpKQpgYGAKCgpgYGAje3J9CgojIHRoZSBwcm9ibGVtIGhlcmUgd2FzIHRoYXQgdGhlIGdlbmUgYW5ub3RhdGlvbnMgb2YgdGhlIGV4cHJlc3Npb24gbWF0cml4CmRpZCBub3QgbWF0Y2ggdGhlIGdlbmUgYW5ub3RhdGlvbnMgb2YgdGhlIEFyY2hyIG9iamVjdC4KCmRmIDwtIHJvd0RhdGEocm5hX292ZXJsYXApICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGZpbHRlcihnZW5lICVpbiVnZW5lcykKZGYKCmdyYW5nZXMgPC0gZGYgJT4lIEdSYW5nZXMoKQpncmFuZ2VzCgoKYGBgCgpXZSB3aWxsIG5vdyBhZGQgdGhlIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggdG8gdGhlIEFyY2hSIG9iamVjdC4gRmlyc3QsIHdlIGhhdmUgdG8gCmNvbnZlcnQgdGhlIFNpbmdsZSBDZWxsIEV4cGVyaW1lbnQgdG8gYSBTdW1tYXJpemVkIEV4cGVyaW1lbnQuCgpgYGB7cn0KIyBjcmVhdGUgc3VtbWFyaXplZCBleHBlcmltZW50IG9mIHRoZSBzY1JOQS1zZXEKc2VSTkEgPC0gU3VtbWFyaXplZEV4cGVyaW1lbnQoYXNzYXlzID0gbGlzdChjb3VudHM9cm5hX2NvdW50cyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbG5hbWVzKHJuYV9vdmVybGFwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3dSYW5nZXMgPSBncmFuZ2VzKQpzZVJOQQoKCiMgYWRkIFJOQSBnZW5lIGV4cHJlc3Npb24gbWF0cml4IHRvIHRoZSBBcmNoUiBvYmplY3QKZmlsdF9wcm9qIDwtIGFkZEdlbmVFeHByZXNzaW9uTWF0cml4KGlucHV0ID0gZmlsdF9wcm9qLCBzZVJOQSA9IHNlUk5BLCBmb3JjZSA9IFRSVUUpCgoKIyBzYXZlIHRoZSBBcmNoUiBvYmplY3Qgd2l0aCB0aGUgYWRkZWQgZ2VuZSBleHByZXNzaW9uIG1hdHJpeAojc2F2ZUFyY2hSUHJvamVjdChmaWx0X3Byb2osIG91dHB1dERpcmVjdG9yeSA9ICIwM19hZGRlZF9nZW5lX2V4cHJlc3Npb25fQXJjaFJvYmplY3QiLCBsb2FkID0gRkFMU0UpCmBgYAoKCgpXZSB3aWxsIGFkZCBhIHNlY29uZCBMU0kgZm9yIHRoZSBSTkEgZGF0YS4gVGhlIG9uZSBiYXNlZCBvbiB0aGUgQVRBQyBkYXRhIGlzIGNhbGxlZCAKIkxTSV9BVEFDIi4gV2Ugd2lsbCBjYWxsIHRoZSBvbmUgZm9yIHRoZSBSTkEgZGF0YSAiTFNJX1JOQSIKCmBgYCN7cn0KZmlsdF9wcm9qIDwtIGFkZEl0ZXJhdGl2ZUxTSSgKICAgIEFyY2hSUHJvaiA9IGZpbHRfcHJvaiwgCiAgICBjbHVzdGVyUGFyYW1zID0gbGlzdCgKICAgICAgcmVzb2x1dGlvbiA9IDAuMiwgCiAgICAgIHNhbXBsZUNlbGxzID0gMTAwMDAsCiAgICAgIG4uc3RhcnQgPSAxMAogICAgKSwKICAgIHNhdmVJdGVyYXRpb25zID0gRkFMU0UsCiAgICB1c2VNYXRyaXggPSAiR2VuZUV4cHJlc3Npb25NYXRyaXgiLCAKICAgIGRlcHRoQ29sID0gIkdleF9uVU1JIiwKICAgIHZhckZlYXR1cmVzID0gMjUwMCwKICAgIGZpcnN0U2VsZWN0aW9uID0gInZhcmlhYmxlIiwKICAgIGJpbmFyaXplID0gRkFMU0UsCiAgICBuYW1lID0gIkxTSV9STkEiCikKYGBgCgoKYGBgI3tyfQpmaWx0X3Byb2ogPC0gYWRkSXRlcmF0aXZlTFNJKAogIEFyY2hSUHJvaiA9IGZpbHRfcHJvaiwgCiAgc2F2ZUl0ZXJhdGlvbnMgPSBGQUxTRSwKICBjbHVzdGVyUGFyYW1zICA9IGxpc3QoCiAgICByZXNvbHV0aW9uID0gMC4yLAogICAgc2FtcGxlQ2VsbHMgPSAxMDAwMCwKICAgIG4uc3RhcnQgPSAxMAogICksCiAgdXNlTWF0cml4ID0gIkdlbmVFeHByZXNzaW9uTWF0cml4IiwKICBkZXB0aENvbCA9ICJHZXhfblVNSSIsCiAgdmFyRmVhdHVyZXMgPSAxMDAwMDAsIAogIGZpcnN0U2VsZWN0aW9uID0gIlZhciIsIAogIGJpbmFyaXplID0gRkFMU0UsCiAgbmFtZSA9ICJMU0lfUk5BIgopCmBgYAoKCk5vdyB3ZSB3aWxsIGNvbWJpbmUgdGhlIHR3byBlbWJlZGRpbmdzLgoKYGBgI3tyfQojQ29tYmluZWQgRGltcwpwcm9qIDwtIGFkZENvbWJpbmVkRGltcyhwcm9qLCByZWR1Y2VkRGltcyA9IGMoIkxTSV9BVEFDIiwgIkxTSV9STkEiKSwgbmFtZSA9ICAiTFNJX0NvbWJpbmVkIikKCiNVTUFQcwpwcm9qIDwtIGFkZFVNQVAocHJvaiwgcmVkdWNlZERpbXMgPSAiTFNJX0FUQUMiLCBuYW1lID0gIlVNQVBfQVRBQyIsIG1pbkRpc3QgPSAwLjgsIGZvcmNlID0gVFJVRSkKCnByb2ogPC0gYWRkVU1BUChwcm9qLCByZWR1Y2VkRGltcyA9ICJMU0lfUk5BIiwgbmFtZSA9ICJVTUFQX1JOQSIsIG1pbkRpc3QgPSAwLjgsIGZvcmNlID0gVFJVRSkKCnByb2ogPC0gYWRkVU1BUChwcm9qLCByZWR1Y2VkRGltcyA9ICJMU0lfQ29tYmluZWQiLCBuYW1lID0gIlVNQVBfQ29tYmluZWQiLCBtaW5EaXN0ID0gMC44LCBmb3JjZSA9IFRSVUUpCgoKYGBgCgo=